83 lines
3.7 KiB
C#
83 lines
3.7 KiB
C#
using dy.net.model.dto;
|
|
|
|
namespace dy.net.storage
|
|
{
|
|
public class LocalMediaStorage : IMediaStorage
|
|
{
|
|
public StorageType StorageType => StorageType.Local;
|
|
|
|
public Task EnsureDirectoryAsync(string path, CancellationToken cancellationToken = default)
|
|
{
|
|
if (!string.IsNullOrWhiteSpace(path)) Directory.CreateDirectory(path);
|
|
return Task.CompletedTask;
|
|
}
|
|
|
|
public Task<bool> ExistsAsync(string path, CancellationToken cancellationToken = default) =>
|
|
Task.FromResult(File.Exists(path));
|
|
|
|
public Task<long?> GetLengthAsync(string path, CancellationToken cancellationToken = default) =>
|
|
Task.FromResult<long?>(File.Exists(path) ? new FileInfo(path).Length : null);
|
|
|
|
public async Task WriteAsync(string path, Stream source, long? contentLength = null, string contentType = null, CancellationToken cancellationToken = default)
|
|
{
|
|
var directory = Path.GetDirectoryName(path);
|
|
if (!string.IsNullOrWhiteSpace(directory)) Directory.CreateDirectory(directory);
|
|
|
|
var tempPath = path + ".part-" + Guid.NewGuid().ToString("N");
|
|
try
|
|
{
|
|
await using (var output = new FileStream(tempPath, FileMode.CreateNew, FileAccess.Write, FileShare.None, 81920, true))
|
|
{
|
|
await source.CopyToAsync(output, 81920, cancellationToken);
|
|
await output.FlushAsync(cancellationToken);
|
|
}
|
|
var actualLength = new FileInfo(tempPath).Length;
|
|
if (actualLength <= 0) throw new IOException("本地写入后的临时文件为空");
|
|
if (contentLength.HasValue && actualLength != contentLength.Value)
|
|
throw new IOException($"本地写入长度不一致:期望 {contentLength.Value},实际 {actualLength}");
|
|
File.Move(tempPath, path, true);
|
|
var finalLength = new FileInfo(path).Length;
|
|
if (finalLength != actualLength) throw new IOException($"本地最终文件长度不一致:期望 {actualLength},实际 {finalLength}");
|
|
}
|
|
finally
|
|
{
|
|
if (File.Exists(tempPath)) File.Delete(tempPath);
|
|
}
|
|
}
|
|
|
|
public Task<StorageReadResult> OpenReadAsync(string path, long? from = null, long? to = null, CancellationToken cancellationToken = default)
|
|
{
|
|
if (!File.Exists(path)) throw new FileNotFoundException("媒体文件不存在", path);
|
|
var file = new FileInfo(path);
|
|
var start = Math.Max(0, from ?? 0);
|
|
var end = Math.Min(file.Length - 1, to ?? file.Length - 1);
|
|
var stream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read, 81920, true);
|
|
stream.Seek(start, SeekOrigin.Begin);
|
|
return Task.FromResult(new StorageReadResult
|
|
{
|
|
Stream = stream,
|
|
ContentLength = end - start + 1,
|
|
ContentType = MimeType(path),
|
|
ContentRange = from.HasValue ? $"bytes {start}-{end}/{file.Length}" : null,
|
|
StatusCode = from.HasValue ? 206 : 200
|
|
});
|
|
}
|
|
|
|
public Task DeleteAsync(string path, CancellationToken cancellationToken = default)
|
|
{
|
|
if (File.Exists(path)) File.Delete(path);
|
|
return Task.CompletedTask;
|
|
}
|
|
|
|
private static string MimeType(string path) => Path.GetExtension(path).ToLowerInvariant() switch
|
|
{
|
|
".mp4" => "video/mp4",
|
|
".jpg" or ".jpeg" => "image/jpeg",
|
|
".png" => "image/png",
|
|
".mp3" => "audio/mpeg",
|
|
".nfo" or ".xml" => "application/xml",
|
|
_ => "application/octet-stream"
|
|
};
|
|
}
|
|
}
|