.NET Web API Blueimp 多文件上传错误“意外的 MIME 多部分流结束MIME 多部分消息不完整”

问题描述

我正在构建一个视频管理应用,允许将多个视频上传到 Azure 存储,然后由 Azure 媒体服务进行编码。

我的问题是,如果我使用 blueimp 一次只上传 1 个文件,则一切正常。当我向上传添加多个文件时,第二个文件出现错误

MIME 多部分流意外结束。 MIME 多部分消息不完整。

我已经读到这可能是因为流缺少文件终止符,所以我在建议的调整中添加了行终止符(根据本文 ASP.NET Web API,unexpected end of MIME multi-part stream when uploading from Flex FileReference),但没有运气。

如果我作为单个文件发布(通过迭代选择上传文件)并将它们作为单独的帖子发送,它就可以工作。我的问题是我想选择多个文件以及添加额外的元数据并点击一个提交按钮。当我这样做时,第一个文件上传,第二个文件似乎开始上传,但随后我收到错误 500“MIME 多部分流意外结束。MIME 多部分消息未完成”消息。

这是我的上传代码(Web API):

[HttpPost]
    public async Task<HttpResponseMessage> UploadMedia()
    {
        HttpResponseMessage result = null;
        var httpRequest = HttpContext.Current.Request;
        if (httpRequest.Headers["content-type"] != null)
        {
            httpRequest.Headers.Remove("content-type");
        }
        httpRequest.Headers.Add("enctype","multipart/form-data");
        if (httpRequest.Files.Count > 0)
        {
            var docfiles = new List<string>();
            foreach (string file in httpRequest.Files)
            {
                var postedFile = httpRequest.Files[file];
                var filePath = HttpContext.Current.Server.MapPath("~/" + postedFile.FileName);
                string assignedSectionList = string.Empty;
                postedFile.SaveAs(filePath);
                docfiles.Add(filePath);

                string random = Helpers.Helper.RandomDigits(10).ToString();

                string ext = System.IO.Path.GetExtension(filePath);

                string newFileName = (random + ext).ToLower();

                MediaType mediaType = MediaType.Video;
                if (newFileName.Contains(".mp3"))
                {
                    mediaType = MediaType.Audio;
                }

               if (httpRequest.Form["sectionList"] != null)
                {
                    assignedSectionList = httpRequest.Form["sectionList"];
                }

                MediaUploadQueue mediaUploadQueueItem = new MediaUploadQueue();
                mediaUploadQueueItem.OriginalFileName = postedFile.FileName;
                mediaUploadQueueItem.FileName = newFileName;
                mediaUploadQueueItem.UploadedDateTime = DateTime.UtcNow;
                mediaUploadQueueItem.LastUpdatedDateTime = DateTime.UtcNow;
                mediaUploadQueueItem.Status = "pending";
                mediaUploadQueueItem.Size = postedFile.ContentLength;
                mediaUploadQueueItem.Identifier = random;
                mediaUploadQueueItem.MediaType = mediaType;
                mediaUploadQueueItem.AssignedSectionList = assignedSectionList;
                db.MediaUploadQueue.Add(mediaUploadQueueItem);
                db.SaveChanges();

     

                byte[] chunk = new byte[httpRequest.ContentLength];
                httpRequest.InputStream.Read(chunk,Convert.ToInt32(httpRequest.ContentLength));
                var provider = new AzureStorageMultipartFormDataStreamProviderNoMod(new AzureMediaServicesHelper().container);
                provider.fileNameOverride = newFileName;
                await Request.Content.ReadAsMultipartAsync(provider); //this uploads it to the storage account
                

                //AzureMediaServicesHelper amsHelper = new AzureMediaServicesHelper();
                string assetId = amsHelper.CommitAsset(mediaUploadQueueItem); //begin the process of encoding the file

                mediaUploadQueueItem.AssetId = assetId;
                db.SaveChanges();

                ////start the encoding
                amsHelper.EncodeAsset(assetId);

            }
            result = Request.CreateResponse(HttpStatusCode.Created,docfiles);

        }
        else
        {
            result = Request.CreateResponse(HttpStatusCode.BadRequest);
        }
        return result;
    }

这是发送到 Azure Blob 存储的上传处理程序的代码

    public override Stream GetStream(HttpContent parent,HttpContentHeaders headers)
    {
        if (parent == null) throw new ArgumentNullException(nameof(parent));
        if (headers == null) throw new ArgumentNullException(nameof(headers));

        if (!_supportedMimeTypes.Contains(headers.ContentType.ToString().ToLower()))
        {
            throw new NotSupportedException("Only jpeg and png are supported");
        }

        // Generate a new filename for every new blob
        var fileName = Guid.NewGuid().ToString();

        if (!String.IsNullOrEmpty(fileNameOverride))
            fileName = fileNameOverride;

        CloudBlockBlob blob = _blobContainer.GetBlockBlobReference(fileName);

        if (headers.ContentType != null)
        {
            // Set appropriate content type for your uploaded file
            blob.Properties.ContentType = headers.ContentType.MediaType;
        }

        this.FileData.Add(new multipartfileData(headers,blob.Name));

        return blob.OpenWrite();
    }

这是javascript代码。第一个是将文件单独作为单独的帖子发送,它有效。

$("#fileupload").fileupload({
    autoUpload: false,dataType: "json",add: function (e,data) {
        data.context = $('<p class="file">')
            .append($('<a target="_blank">').text(data.files[0].name))
            .appendTo(document.body);
        data.submit();
    },progress: function (e,data) {
        var progress = parseInt((data.loaded / data.total) * 100,10);
        data.context.css("background-position-x",100 - progress + "%");
    },done: function (e,data) {
        data.context
            .addClass("done")
            .find("a")
            .prop("href",data.result.files[0].url);
    }
});

下面的代码不起作用。它将所有文件推入数组并在一个帖子中发送它们。这个在第二个文件上失败了。如果我使用此代码上传一个文件,它就可以工作。

var filesList = new Array();
$(function () {
    $('#fileupload').fileupload({
        autoUpload: false,dropZone: $('#dropzone'),data) {
            filesList.push(data.files[0]);
            data.context = $('<div class="file"/>',{ class: 'thumbnail pull-left' }).appendTo('#files');
            var node = $('<p />').append($('<span/>').text(data.files[0].name).data(data));
            node.appendTo(data.context);
        },data) { //Still working on this part
            //var progress = parseInt((data.loaded / data.total) * 100,10);
            //data.context.css("background-position-x",100 - progress + "%");
        },}).on('fileuploadprocessalways',function (e,data) {
        var index = data.index,file = data.files[index],node = $(data.context.children()[index]);
        if (file.preview) {
            node.prepend('<br>').prepend(file.preview);
        }
        if (file.error) {
            node.append('<br>').append($('<span class="text-danger"/>').text(file.error));
        }
    }).prop('disabled',!$.support.fileInput)
        .parent().addClass($.support.fileInput ? undefined : 'disabled');
    $("#uploadform").submit(function (event) {
        if (filesList.length > 0) {
            console.log("multi file submit");
            event.preventDefault();
            $('#fileupload').fileupload('send',{ files: filesList })
                .success(function (result,textStatus,jqXHR) { console.log('success'); })
                .error(function (jqXHR,errorThrown) { console.log('error'); })
                .complete(function (result,jqXHR) {
                    console.log('complete: ' + JSON.stringify(result)); //The error 500 is returned here. In fiddler,it shows and error 500. If I try to trap in Visual Studio,I can't seem to pinpoint the exception.
                    // window.location='back to view-page after submit?'
                });
        } else {
            console.log("plain default form submit");
        }
    });
});

对为什么会发生这种情况有任何想法吗?我已经尝试了所有我能想到的方法,但都没有运气。提前致谢!

解决方法

我想指出您的代码架构可能会导致超时或错误。

我会首先将所有内容上传到 azure 存储,将状态存储在缓存或数据库中。

然后我会启动一个后台作业(hangfire、azure 函数、webjobs)来处理上传到媒体服务的其他工作。

我建议从用户输入异步执行此操作。

根据 dropzone 的文档,确保在 HTML 标签中添加名称

<form action="/file-upload" class="dropzone">
  <div class="fallback">
    <input name="file" type="file" multiple />
  </div>
</form>

如果您以编程方式执行此操作:

function param() {
                return "files";
            }

 Dropzone.options.myDropzone = {
                uploadMultiple: true,paramName: param,}

在后端,您需要在每个流后添加 \r\n: