125 lines
4.6 KiB
C#
125 lines
4.6 KiB
C#
using IM_API.Dtos;
|
|
using IM_API.Interface.Services;
|
|
using IM_API.Models.Upload;
|
|
using IM_API.Tools;
|
|
using IM_API.VOs;
|
|
using Microsoft.AspNetCore.Authorization;
|
|
using Microsoft.AspNetCore.Http;
|
|
using Microsoft.AspNetCore.Mvc;
|
|
using Microsoft.EntityFrameworkCore.Storage;
|
|
using StackExchange.Redis;
|
|
using System.Text;
|
|
using System.Text.Json;
|
|
using IDatabase = StackExchange.Redis.IDatabase;
|
|
|
|
namespace IM_API.Controllers
|
|
{
|
|
[Authorize]
|
|
[Route("api/[controller]")]
|
|
[ApiController]
|
|
public class UploadController : ControllerBase
|
|
{
|
|
private readonly IWebHostEnvironment _env;
|
|
private readonly IStorageService _storage;
|
|
private readonly IDatabase _redis;
|
|
public UploadController(IWebHostEnvironment env, IStorageService storage, IConnectionMultiplexer connectionMultiplexer)
|
|
{
|
|
_env = env;
|
|
_storage = storage;
|
|
_redis = connectionMultiplexer.GetDatabase();
|
|
}
|
|
[HttpPost("local/{taskId}/parts/{partNumber}")]
|
|
[ProducesResponseType(typeof(BaseResponse<object?>), StatusCodes.Status200OK)]
|
|
public async Task<IActionResult> LocalUpload(Guid taskId, int partNumber, IFormFile file)
|
|
{
|
|
var baseDir = Path.Combine(_env.ContentRootPath, "uploads"); // 项目根目录下 uploads
|
|
Directory.CreateDirectory(baseDir);
|
|
|
|
var path = Path.Combine(baseDir, "temp", taskId.ToString(), $"{partNumber}.part.tmp");
|
|
Directory.CreateDirectory(Path.GetDirectoryName(path)!);
|
|
|
|
using var stream = System.IO.File.Create(path);
|
|
await file.CopyToAsync(stream);
|
|
|
|
await _redis.SetAddAsync(RedisKeys.GetUploadPartKey(taskId), partNumber);
|
|
|
|
return Ok(new BaseResponse<object?>());
|
|
}
|
|
|
|
[HttpPost("CreateTask")]
|
|
[ProducesResponseType(typeof(BaseResponse<CreateUploadTaskVo>), StatusCodes.Status200OK)]
|
|
public async Task<IActionResult> CreateUpload(CreateUploadTaskDto dto)
|
|
{
|
|
var vo = await _storage.InitTaskAsync(dto);
|
|
return Ok(new BaseResponse<CreateUploadTaskVo>(vo));
|
|
}
|
|
|
|
[HttpPost("CreatePart")]
|
|
public async Task<IActionResult> CreatePart(Guid taskId, int partNum)
|
|
{
|
|
var vo = await _storage.CreatePartInstructionAsync(taskId, partNum);
|
|
return Ok(new BaseResponse<UploadPartInstructionVo>(vo));
|
|
}
|
|
|
|
[HttpPost("CompleteTask")]
|
|
public async Task<IActionResult> CompleteTask([FromQuery]Guid taskId, [FromBody]List<UploadPartDto> dtos)
|
|
{
|
|
var taskIdRes = await _storage.CompleteAsync(taskId, dtos);
|
|
return Ok(new BaseResponse<string>(data: taskIdRes.ToString()));
|
|
}
|
|
|
|
[HttpGet("events/{taskId}")]
|
|
[AllowAnonymous]
|
|
public async Task Events(Guid taskId)
|
|
{
|
|
Response.Headers.Add("Content-Type", "text/event-stream");
|
|
Response.Headers.Add("Cache-Control", "no-cache");
|
|
Response.Headers.Add("Connection", "keep-alive");
|
|
var lastProgress = -1;
|
|
|
|
while (!HttpContext.RequestAborted.IsCancellationRequested)
|
|
{
|
|
var hash = await _redis.HashGetAllAsync(RedisKeys.MergeStatus(taskId));
|
|
if (hash.Length == 0)
|
|
{
|
|
await Task.Delay(1000);
|
|
continue;
|
|
}
|
|
var status = hash.FirstOrDefault(x => x.Name == "status").Value;
|
|
var progress = hash.FirstOrDefault(x => x.Name == "progress").Value;
|
|
var url = hash.FirstOrDefault(x => x.Name == "url").Value;
|
|
|
|
// 避免重复发送
|
|
if (progress != lastProgress)
|
|
{
|
|
var data = new
|
|
{
|
|
status = status.ToString(),
|
|
progress = progress.ToString(),
|
|
url = (string)url
|
|
};
|
|
|
|
await Response.WriteAsync($"data: {JsonSerializer.Serialize(data)}\n\n");
|
|
await Response.Body.FlushAsync();
|
|
|
|
// 完成后关闭 SSE
|
|
if (status == "Completed")
|
|
break;
|
|
|
|
await Task.Delay(1000); // 每秒检查一次
|
|
}
|
|
}
|
|
}
|
|
|
|
[HttpPost("upload/{hash}")]
|
|
public async Task<IActionResult> UploadSmallFile(IFormFile file,string hash)
|
|
{
|
|
using var stream = file.OpenReadStream();
|
|
var res = await _storage.UploadSmallFileAsync(stream, file.FileName, file.ContentType, file.Length, hash);
|
|
return Ok(new BaseResponse<UploadTask>(res));
|
|
}
|
|
|
|
|
|
}
|
|
}
|