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

Posted

技术标签:

【中文标题】.NET Web API Blueimp 多文件上传错误“MIME 多部分流意外结束。MIME 多部分消息不完整。”【英文标题】:.NET Web API Blueimp multiple file upload error "Unexpected end of MIME multipart stream. MIME multipart message is not complete." 【发布时间】:2021-05-18 19:00:01 【问题描述】:

我正在构建一个视频管理应用程序,它允许将多个视频上传到 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, 0, 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'),
        add: function (e, 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);
        ,
        progress: function (e, 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, textStatus, errorThrown)  console.log('error'); )
                .complete(function (result, textStatus, 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");
        
    );
);

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

【问题讨论】:

【参考方案1】:

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

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

然后我会启动一个后台作业(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:

【讨论】:

以上是关于.NET Web API Blueimp 多文件上传错误“MIME 多部分流意外结束。MIME 多部分消息不完整。”的主要内容,如果未能解决你的问题,请参考以下文章

Vimeo API:使用 HTTP PUT 和 blueimp 的 jQuery fileupload 进行流式上传

Asp.Net Web Api 2 实现多文件打包并下载文件示例源码_转

BlueImp 文件上传“错误:方法不允许” - Laravel 4 路由问题

通过 https 上传多个文件时 blueimp jquery 文件上传挂起

分片上传

如何在webpack上使用blueimp-file-upload?