如何使用JQuery File Upload实现Web API控制器以接受分块上传

本文关键字:控制器 API JQuery 何使用 File Upload Web 实现 | 更新日期: 2023-09-27 18:27:51

正如标题所述,我需要一些帮助来实现Web API控制器,以使用JQuery File Upload接受分块上传。任何帮助(包括现有文章/教程的链接)都将不胜感激。

如何使用JQuery File Upload实现Web API控制器以接受分块上传

首先从客户端开始。您必须为分块上传设置maxChunkSize选项。之后,每个文件都需要一个唯一的标识符,以便识别服务器上的每个块,并将相应的块数据附加到正确的文件中。

$('#fileupload')
        .bind('fileuploadsubmit', function (e, data) {
            data.headers = $.extend(data.headers,
                {"X-File-Identifier": generateFileUniqueIdentifier(data)})
            });
        });
generateFileUniqueIdentifier = function(data){
    var file=data.files[0],
    var result = file.relativePath||file.webkitRelativePath||file.fileName||file.name;
    
    return result.replace(/[^0-9a-zA-Z_-]/img,"") + "-" + i.size + "-" + $.now()
} 

现在在服务器端:ApiController

 public class UploadController : ApiController
 {
        [HttpPost]
        [Route("upload/{targetFolder:int}")]
        [ValidateMimeMultipartContentFilter]
        public async Task<IHttpActionResult> UploadDocument(int targetFolder)
        {
            var uploadFileService = new UploadFileService();
            UploadProcessingResult uploadResult = await uploadFileService.HandleRequest(Request);
            if (uploadResult.IsComplete)
            {
                // do other stuff here after file upload complete    
                return Ok();
            }
            return Ok(HttpStatusCode.Continue);
        }
}

实际上载文件的服务类。这支持块或整个文件。

public class UploadFileService
{
        private readonly string _uploadPath;
        private readonly MultipartFormDataStreamProvider _streamProvider;
        public UploadFileService()
        {
            _uploadPath = UserLocalPath;
            _streamProvider = new MultipartFormDataStreamProvider(_uploadPath);
        }
    
        #region Interface
        public async Task<UploadProcessingResult> HandleRequest(HttpRequestMessage request)
        {
            await request.Content.ReadAsMultipartAsync(_streamProvider);
            return await ProcessFile(request);
        }
        #endregion    
        #region Private implementation
        private async Task<UploadProcessingResult> ProcessFile(HttpRequestMessage request)
        {
            if (request.IsChunkUpload())
            {
                return await ProcessChunk(request);
            }
            return new UploadProcessingResult()
            {
                IsComplete = true,
                FileName = OriginalFileName,
                LocalFilePath = LocalFileName,
                FileMetadata = _streamProvider.FormData
            };
        }
        private async Task<UploadProcessingResult> ProcessChunk(HttpRequestMessage request)
        {
            //use the unique identifier sent from client to identify the file
            FileChunkMetaData chunkMetaData = request.GetChunkMetaData();
            string filePath = Path.Combine(_uploadPath, string.Format("{0}.temp", chunkMetaData.ChunkIdentifier));
            //append chunks to construct original file
            using (FileStream fileStream = new FileStream(filePath, FileMode.OpenOrCreate | FileMode.Append))
            {
                var localFileInfo = new FileInfo(LocalFileName);
                var localFileStream = localFileInfo.OpenRead();
                await localFileStream.CopyToAsync(fileStream);
                await fileStream.FlushAsync();
                fileStream.Close();
                localFileStream.Close();
                //delete chunk
                localFileInfo.Delete();
            }
            return new UploadProcessingResult()
            {
                IsComplete = chunkMetaData.IsLastChunk,
                FileName = OriginalFileName,
                LocalFilePath = chunkMetaData.IsLastChunk ? filePath : null,
                FileMetadata = _streamProvider.FormData
            };
        }
        #endregion    
        #region Properties
        private string LocalFileName
        {
            get
            {
                MultipartFileData fileData = _streamProvider.FileData.FirstOrDefault();
                return fileData.LocalFileName;
            }
        }
        private string OriginalFileName
        {
            get
            {
                MultipartFileData fileData = _streamProvider.FileData.FirstOrDefault();
                return fileData.Headers.ContentDisposition.FileName.Replace("'"", string.Empty);
            }
        }
        private string UserLocalPath
        {
            get
            {
               //return the path where you want to upload the file                   
            }
        }
        #endregion    
    }

HttpRequestMessagge上用于识别区块请求的扩展

public static class HttpRequestMessageExtensions
{
        public static bool IsChunkUpload(this HttpRequestMessage request)
        {
            return request.Content.Headers.ContentRange != null;
        }
        public static FileChunkMetaData GetChunkMetaData(this HttpRequestMessage request)
        {
            return new FileChunkMetaData()
            {
                ChunkIdentifier = request.Headers.Contains("X-DS-Identifier") ? request.Headers.GetValues("X-File-Identifier").FirstOrDefault() : null,
                ChunkStart = request.Content.Headers.ContentRange.From,
                ChunkEnd = request.Content.Headers.ContentRange.To,
                TotalLength = request.Content.Headers.ContentRange.Length
            };
        }
    }

最后给出了服务响应模型和块元数据

public class FileChunkMetaData
{
    public string ChunkIdentifier { get; set; }
    public long? ChunkStart { get; set; }
    public long? ChunkEnd { get; set; }
    public long? TotalLength { get; set; }
    public bool IsLastChunk
    {
        get { return ChunkEnd + 1 >= TotalLength; }
    }
}
public class UploadProcessingResult
{
    public bool IsComplete { get; set; }
    public string FileName { get; set; }
    public string LocalFilePath { get; set; }
    public NameValueCollection FileMetadata { get; set; }
}

MultiPartContentFilter只是一个ActionFilter来验证内容(来自damienbod)

public class ValidateMimeMultipartContentFilter : ActionFilterAttribute
{
    public override void OnActionExecuting(HttpActionContext actionContext)
    {
        if (!actionContext.Request.Content.IsMimeMultipartContent())
        {
            throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType);
        }
    }
    public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
    {
    }
}

您可以在这里找到灵感:ASP.NET多文件拖动上传&使用HTML5。分块上传控制器方法的示例从UploadFile开始。在客户端上,jquery文件上传选项maxChunkSize需要根据https://github.com/blueimp/jQuery-File-Upload/wiki/Options。