使用MVC API POST Multipart表单上传图像

时间:2014-01-30 11:30:09

标签: asp.net-mvc file-upload task-parallel-library multipartform-data

我的观点: -

   <form class="commentform commentright"   enctype="multipart/form-data" >
                <textarea class="simpleta required" name="Body" placeholder="Discuss this vehicle with other members of Row52."></textarea>
                <input type="file" id="fileuploadfield" name="fileuploadfield"/>
                <input type="submit" value="Submit" class="btn btn-inverse btn-small"/>
                <input type="hidden" name="ParentId" value="0"/>
                <input type="hidden" name="VehicleId" value="@Model.VehicleId"/>
                <span class="commentcount"><span class="numberofcomments">@Model.Count</span>@count</span>
                <div class="feedback"></div>
                <img id="img" src="" />

            </form>

JQuery: -

 submitComment: function (form, type) {
            var self = this;
            var $this = $(form);
            var formData = $this.serialize();
            var $message = $this.find('.feedback');

            $message.hide();
            $message.html('');

            var val = validation({ $form: $this });
            if (val.checkRequired()) {
                $this.indicator({ autoStart: false, minDuration: 100 });
                $this.indicator('start');
                var files = $("#fileuploadfield").get(0).files;

                if (files.length > 0) {
                    if (window.FormData !== undefined) {
                        var data = new FormData();
                        for (var i = 0; i < files.length; i++) {
                            data.append("file" + i, files[i]);

                        }
                    }
                    else {
                        alert("This browser doesn't support HTML5 multiple file uploads!");
                    }
                }
                else {
                    alert("This");
                }

                $.ajax('/api/v2/comment/Post/', {
                    type: 'POST',
                    contentType: 'multipart/form-data',
                        // I have also use contentType: false,
                    processData: false,
                    data: formData
                }).done(function (d) {

控制器: -

 public Task<HttpResponseMessage> Post()
        {
            var provider = new MultipartMemoryStreamProvider();
            var task1 = Request.Content.ReadAsMultipartAsync(provider);
            var userId = User.Id;

            return task1.Then(providerResult =>
                {
                    var file = GetFileContent(providerResult);
                    var task2 = file.ReadAsStreamAsync();
                    string originalFileName = file.Headers.ContentDisposition.FileName.Replace("\"", "");
                    string extension = Path.GetExtension(originalFileName);
                    string fileName = Guid.NewGuid().ToString() + extension;

                    return task2.Then(stream =>
                        {
                            if (stream.Length > 0)
                            {
                                var kernel = WindsorContainerFactory.Create(KernelState.Thread, ConfigurationState.Web);
                                CloudBlobContainer container = GetContainer();

                                var userRepo = kernel.Resolve<IUserRepository>();
                                var logger = kernel.Resolve<ILogger>();
                                logger.Fatal("Original File Name: " + originalFileName);
                                logger.Fatal("Extension: " + extension);
                                logger.Fatal("File Name: " + fileName);
                                var user = userRepo.FirstOrDefault(x => x.Id == userId);
                                var path = CreateImageSize(stream, 500, fileName, userId, container, logger);
                                if (user != null)
                                {
                                    user.AvatarOriginalFileName = fileName;
                                    user.AvatarOriginalAbsolutePath =
                                        ConfigurationManager.AppSettings["CdnUserEndpointUrl"] + path;
                                    user.AvatarOriginalRelativePath = path;
                                    user.AvatarCroppedAbsolutePath = "";
                                    user.AvatarCroppedFileName = "";
                                    user.AvatarCroppedRelativePath = "";
                                    userRepo.Update(user);
                                }
                                else
                                {
                                    Logger.Error("User is null.  Id: " + userId.ToString());
                                }

                                kernel.Release(userRepo);
                                kernel.Release(logger);
                                kernel.Dispose();
                            }

                            var response = Request.CreateResponse(HttpStatusCode.Moved);
                            response.Headers.Location = new Uri("/Account/Avatar", UriKind.Relative);
                            return response;
                        });
                });
        }

以下错误获取: - “提供的'HttpContent'实例无效。它没有以'multipart /'开头的内容类型标题。参数名称:内容”

enter image description here

1 个答案:

答案 0 :(得分:3)

.NET框架中没有标准的Task.Then方法,我在您发布的代码中找不到任何实现细节。我假设它是从这里拿走的:

Processing Sequences of Asynchronous Operations with Tasks

如果是这种情况,您可能会在AspNetSynchronizationContext lambdas中丢失Task.Then上下文,因为如果没有正确的同步上下文,可能会在不同的ASP.NET池线程上发生延续。这可以解释为什么HttpContent无效。

尝试更改Task.Then的实现,如下所示:

public static Task<T2> Then<T1, T2>(this Task<T1> first, Func<T1, Task<T2>> next) 
{ 
    if (first == null) throw new ArgumentNullException("first"); 
    if (next == null) throw new ArgumentNullException("next");

    var tcs = new TaskCompletionSource<T2>(); 
    first.ContinueWith(delegate 
    { 
        if (first.IsFaulted) tcs.TrySetException(first.Exception.InnerExceptions); 
        else if (first.IsCanceled) tcs.TrySetCanceled(); 
        else 
        { 
            try 
            { 
                var t = next(first.Result); 
                if (t == null) tcs.TrySetCanceled(); 
                else t.ContinueWith(delegate 
                { 
                    if (t.IsFaulted) tcs.TrySetException(t.Exception.InnerExceptions); 
                    else if (t.IsCanceled) tcs.TrySetCanceled(); 
                    else tcs.TrySetResult(t.Result); 
                }, TaskScheduler.FromCurrentSynchronizationContext()); 
            } 
            catch (Exception exc) { tcs.TrySetException(exc); } 
        } 
    }, TaskScheduler.FromCurrentSynchronizationContext()); 
    return tcs.Task; 
}

请注意现在如何使用TaskScheduler.FromCurrentSynchronizationContext()安排续集。

理想情况下,如果您可以使用.NET 4.5,那么您应该使用async/awaitUsing Asynchronous Methods in ASP.NET 4.5。这样,线程的同步上下文会自动捕获await个连续。

以下可能是设施移植: Implementing Then with Await