Files
douyin/storage/LocalMediaStorage.cs

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"
};
}
}