在 Asp.Net MVC 中使用 blueimp 分块上传文件

Uploading files in chunks with blueimp in Asp.Net MVC

我尝试将插件 blueimp 放入我的 Asp.Net MVC 应用程序。 我的上传目标是大约 1GB。 服务器端如何处理chunk文件上传文件?

我想你是说来自 blueimp 的 FileUpload jquery 模块。这就是我在我的项目中处理它的方式。我上传不超过 30MB 的大图片。所以这个例子只是关于代码,而不是你需要处理 1GB 文件的事实。

这是 javascript 代码的一部分。没有什么特别的。我只是遵循 FileUpload 文档和示例。我只是发送了更多属性(包括 AntiforgeryToken)- 正确行为不需要这些属性。

$("#file-upload").fileupload({
    url: 'upload-file',
    dataType: 'json',
    autoUpload: false,
    maxChunkSize: 5000000,
    progressInterval: 1000,
    bitrateInterval: 1000
}).on('fileuploadadd', function (e, data) {
    fileData = data; // save data to be able to submit them later

    if (window.File && window.Blob) {
        // update form data
        data.formData = {
            uploadFolder: '/upload-folder/some-guid',
            __RequestVerificationToken: $("#upload-form").find('input[name=__RequestVerificationToken]').val()
        };
    } else {
        // chunk upload not supported
    }
});

$("#file-submit").on('click', function (e) {
    e.preventDefault();
    fileData.submit();
});

在服务器端我有一个模型class:

public class UploadViewRequest
{
    public Guid UploadFolder { get; set; }
    public bool IsChunk { get; set; }
    public int ChunkNumber { get; set; }
    public bool IsFirst { get; set; }
    public bool IsLast { get; set; }
    public HttpPostedFileBase OriginalFile { get; set; }
    public bool JsonAccepted { get; set; }
}

我为此 class 编写了一个自定义模型活页夹,这样我就可以查看它是整个文件还是只是一个块,如果是,我将要处理文件的哪一部分:

public class UploadViewRequestBinder : DefaultModelBinder
{
    public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
        UploadViewRequest model = base.BindModel(controllerContext, bindingContext) as UploadViewRequest;

        string rangeHeader = controllerContext.HttpContext.Request.Headers["Content-Range"];
        if (string.IsNullOrEmpty(rangeHeader))
            model.IsChunk = false;
        else
        {
            model.IsChunk = true;

            Match match = Regex.Match(rangeHeader, "^bytes ([\d]+)-([\d]+)\/([\d]+)$", RegexOptions.IgnoreCase);
            int bytesFrom = int.Parse(match.Groups[1].Value);
            int bytesTo = int.Parse(match.Groups[2].Value);
            int bytesFull = int.Parse(match.Groups[3].Value);

            if (bytesTo == bytesFull)
                model.IsLast = true;
            else
                model.IsLast = false;

            if (bytesFrom == 0)
            {
                model.ChunkNumber = 1;
                model.IsFirst = true;
            }
            else
            {
                int bytesSize = bytesTo - bytesFrom + 1;
                model.ChunkNumber = (bytesFrom / bytesSize) + 1;
                model.IsFirst = false;
            }
        }

        if (controllerContext.HttpContext.Request["HTTP_ACCEPT"] != null && controllerContext.HttpContext.Request["HTTP_ACCEPT"].Contains("application/json"))
            model.JsonAccepted = true;
        else
            model.JsonAccepted = false;

        return model;
    }
}

这是控制器操作方法:

public ActionResult Upload(UploadViewRequest request)
{
    var path = ''; // create path
    FileStatus status = null;

    try
    {
        if (request.IsChunk)
        {
            if (request.IsFirst )
            {
                // do some stuff that has to be done before the file starts uploading
            }

            var inputStream = request.OriginalFile.InputStream;

            using (var fs = new FileStream(path, FileMode.Append, FileAccess.Write))
            {
                var buffer = new byte[1024];

                var l = inputStream.Read(buffer, 0, 1024);
                while (l > 0)
                {
                    fs.Write(buffer, 0, l);
                    l = inputStream.Read(buffer, 0, 1024);
                }

                fs.Flush();
                fs.Close();
            }

            status = new FileStatus(new FileInfo(path));

            if (request.IsLast) 
            {
                // do some stuff that has to be done after the file is uploaded
            }
        }
        else
        {
            file.SaveAs(path);
            status = new FileStatus(new FileInfo(path));
        } 
    } catch {
        status = new FileStatus 
        {
            error = "Something went wrong"
        };
    }

    // this is just a browser json support/compatibility workaround
    if (request.JsonAccepted)
        return Json(status);
    else
        return Json(status, "text/plain");
}

我将 FileStatus class 用作 return 值并将其转换为 json(对于 UploadFile jquery 模块):

public class FileStatus
{
    public const string HandlerPath = "/";

    public string group { get; set; }
    public string name { get; set; }
    public string type { get; set; }
    public int size { get; set; }
    public string progress { get; set; }
    public string url { get; set; }
    public string thumbnail_url { get; set; }
    public string delete_url { get; set; }
    public string delete_type { get; set; }
    public string error { get; set; }

    public FileStatus()
    {
    }

    public FileStatus(FileInfo fileInfo)
    {
        SetValues(fileInfo.Name, (int)fileInfo.Length, fileInfo.FullName);
    }

    public FileStatus(string fileName, int fileLength, string fullPath)
    {
        SetValues(fileName, fileLength, fullPath);
    }

    private void SetValues(string fileName, int fileLength, string fullPath)
    {
        name = fileName;
        type = "image/png";
        size = fileLength;
        progress = "1.0";
        url = HandlerPath + "/file/upload?f=" + fileName;
        delete_url = HandlerPath + "/file/delete?f=" + fileName;
        delete_type = "DELETE";
        thumbnail_url = "/Content/img/generalFile.png";
    }
}