17

如何使用 ASP.NET MVC4 Web Api 上传大文件
并取得进展?

我看到了这篇文章,我了解如何处理上传的文件,但是如何获取进度数据? 如何接受文件 POST

请不要向我发送上传产品的链接。我想了解如何以 MVC4 Web Api 方式处理此问题...这是在 MVC4 WebApi 中处理文件上传的示例代码

    public async Task<HttpResponseMessage> Post()
    {
        if (Request.Content.IsMimeMultipartContent())
        {
            var path = HttpContext.Current.Server.MapPath("~/App_Data");

            var provider = new MultipartFormDataStreamProvider(path);

            await Request.Content.ReadAsMultipartAsync(provider).ContinueWith(t =>
            {
                if (t.IsFaulted || t.IsCanceled)
                    throw new HttpResponseException(HttpStatusCode.InternalServerError);
            });

            return Request.CreateResponse(HttpStatusCode.OK); 
        }
        else
        {
            throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.NotAcceptable, "This request is not properly formatted"));
        }
    }

现在,当

   await Request.Content.ReadAsMultipartAsync(provider)

我怎样才能得到如何加载字节?

4

3 回答 3

20

默认情况下,在两个地方上传文件的大小有限制。一个在请求级别,第二个,如果您在 IIS 上托管,那么在 Web 服务器级别。我添加了这个博客中提到的几个配置,我能够上传一个 36mb 的文件而没有任何问题。我已经发布了下面的片段。

基本上

1.

  <system.web> 
    <httpRuntime maxRequestLength="2097152"/>
  </system.web>

2.

<system.webServer> 
  <security> 
      <requestFiltering> 
         <requestLimits maxAllowedContentLength="2147483648" /> 
      </requestFiltering> 
  </security><system.webServer> 

如果您愿意,很容易找到加载到服务器中的文件的大小。在您的代码中

在读取流中的文件数据时,对于文件数据中的每个项目,您可以读取本地文件名,如下所示。

 string savedFile = fileData.LocalFileName;
 // use the file info class to derive properties of the uploaded file
 FileInfo file = new FileInfo(savedFile);
//this will give the size of the uploaded file 
long size = file.length/1024

希望这可以帮助。我想知道为什么这被标记了?

于 2013-03-23T05:58:56.667 回答
6

我使用这个解决方案:

public class UploadController : ApiController
{
    private static ConcurrentDictionary<string, State> _state = new ConcurrentDictionary<string, State>();

    public State Get(string id)
    {
        State state;

        if (_state.TryGetValue(id, out state))
        {
            return state;
        }

        return null;
    }


    public async Task<HttpResponseMessage> Post([FromUri] string id)
    {
        if (Request.Content.IsMimeMultipartContent())
        {
            var state = new State(Request.Content.Headers.ContentLength);
            if (!_state.TryAdd(id, state))
                throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.Conflict));

            var path = System.Web.Hosting.HostingEnvironment.MapPath("~/App_Data");

            var provider = new FileMultipartStreamProvider(path, state.Start, state.AddBytes);

            await Request.Content.ReadAsMultipartAsync(provider).ContinueWith(t =>
            {
                _state.TryRemove(id, out state);

                if (t.IsFaulted || t.IsCanceled)
                    throw new HttpResponseException(HttpStatusCode.InternalServerError);
            });


            return Request.CreateResponse(HttpStatusCode.OK);
        }
        else
        {
            throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.NotAcceptable, "This request is not properly formatted"));
        }
    }
}


public class State
{
    public long? Total { get; set; }

    public long Received { get; set; }

    public string Name { get; set; }

    public State(long? total = null)
    {
        Total = total;
    }

    public void Start(string name)
    {
        Received = 0;
        Name = name;
    }

    public void AddBytes(long size)
    {
        Received = size;
    }
}

public class FileMultipartStreamProvider : MultipartStreamProvider
{
    private string _rootPath;
    private Action<string> _startUpload;
    private Action<long> _uploadProgress;

    public FileMultipartStreamProvider(string root_path, Action<string> start_upload, Action<long> upload_progress)
        : base()
    {
        _rootPath = root_path;
        _startUpload = start_upload;
        _uploadProgress = upload_progress;
    }

    public override System.IO.Stream GetStream(HttpContent parent, System.Net.Http.Headers.HttpContentHeaders headers)
    {
        var name = (headers.ContentDisposition.Name ?? "undefined").Replace("\"", "").Replace("\\", "_").Replace("/", "_").Replace("..", "_");

        _startUpload(name);

        return new WriteFileStreamProxy(Path.Combine(_rootPath, name), _uploadProgress);
    }

}

public class WriteFileStreamProxy : FileStream
{
    private Action<long> _writeBytes;

    public WriteFileStreamProxy(string file_path, Action<long> write_bytes)
        : base(file_path, FileMode.Create, FileAccess.Write)
    {
        _writeBytes = write_bytes;
    }

    public override void EndWrite(IAsyncResult asyncResult)
    {
        base.EndWrite(asyncResult);

#if DEBUG
        System.Threading.Thread.Sleep(100);
#endif

        if (_writeBytes != null)
            _writeBytes(base.Position);

    }

    public override void Write(byte[] array, int offset, int count)
    {
        base.Write(array, offset, count);

#if DEBUG
        System.Threading.Thread.Sleep(100);
#endif
        if (_writeBytes != null)
            _writeBytes(base.Position);
    }
}

非缓冲输入流的小配置:

config.Services.Replace(typeof(IHostBufferPolicySelector), new CustomPolicy());

实现了这个:

public class CustomPolicy : System.Web.Http.WebHost.WebHostBufferPolicySelector
{
    public override bool UseBufferedInputStream(object hostContext)
    {
        return false;
    }
}
于 2014-02-28T06:49:02.403 回答
0

I Ended Up using an HttpModule but even the HttpModule won't show the progress bar I found out something very interesting it's seems that when i upload the file in a Secure Protocol(over https://) then the progress are working but in non secure protocl (http://) the progress is not working and the file is fully buffered i don't know way is like that i believe it's a bug somewhere between the IIS to Asp.net Framework when the Request are get Procced.

now because i success make it work over https with an HttpModule i believe it is possible to make it work also with Mvc Web Api but i currently don't have the time to check that.

for parsing Mutlipart form data i used Nancy HttpMultipart parser here: https://github.com/NancyFx/Nancy/tree/master/src/Nancy just grabbed the classes:
HttpMultipart.cs
HttpMultipartBoundary.cs
HttpMultipartBuffer.cs
HttpMultipartSubStream.cs

here is the HttpModule Source:

public class HttpUploadModule : IHttpModule
{
    public static DateTime lastClean = DateTime.UtcNow;
    public static TimeSpan cleanInterval = new TimeSpan(0,10,0);
    public static readonly object cleanLocker = new object();

    public static readonly Dictionary<Guid,UploadData> Uploads = new Dictionary<Guid,UploadData>();

    public const int KB = 1024;
    public const int MB = KB * 1024;

    public static void CleanUnusedResources( HttpContext context) 
    {
        if( lastClean.Add( cleanInterval ) < DateTime.UtcNow ) {

            lock( cleanLocker ) 
            {

                if( lastClean.Add( cleanInterval ) < DateTime.UtcNow ) 
                {
                    int maxAge = int.Parse(ConfigurationManager.AppSettings["HttpUploadModule.MaxAge"]);

                    Uploads.Where(u=> DateTime.UtcNow.AddSeconds(maxAge) > u.Value.createdDate ).ToList().ForEach(u=>{    
                        Uploads.Remove(u.Key);
                    });

                    Directory.GetFiles(context.Server.MapPath(ConfigurationManager.AppSettings["HttpUploadModule.Folder"].TrimEnd('/'))).ToList().ForEach(f=>{     
                        if( DateTime.UtcNow.AddSeconds(maxAge) > File.GetCreationTimeUtc(f)) File.Delete(f);
                    });

                    lastClean = DateTime.UtcNow;
                }
            }

        }
    }

    public void Dispose()
    {   

    }

    public void Init(HttpApplication app)
    {
        app.BeginRequest += app_BeginRequest;
    }

    void app_BeginRequest(object sender, EventArgs e)
    {
        HttpContext context = ((HttpApplication)sender).Context;

        Guid uploadId = Guid.Empty;

        if (context.Request.HttpMethod == "POST" && context.Request.ContentType.ToLower().StartsWith("multipart/form-data"))
        {
            IServiceProvider provider = (IServiceProvider)context;
            HttpWorkerRequest wr = (HttpWorkerRequest)provider.GetService(typeof(HttpWorkerRequest));
            FileStream fs = null;
            MemoryStream ms = null;

            CleanUnusedResources(context);                


            string contentType = wr.GetKnownRequestHeader(HttpWorkerRequest.HeaderContentType);
            NameValueCollection queryString = HttpUtility.ParseQueryString( wr.GetQueryString() );

            UploadData upload = new UploadData { id = uploadId ,status = 0, createdDate = DateTime.UtcNow };


            if(
                                    !contentType.Contains("boundary=") ||   
            /*AT LAST 1KB        */ context.Request.ContentLength < KB ||
            /*MAX 5MB            */ context.Request.ContentLength > MB*5 || 
            /*IS UPLOADID        */ !Guid.TryParse(queryString["upload_id"], out uploadId) || Uploads.ContainsKey( uploadId )) {
                upload.id = uploadId;
                upload.status = 2;
                Uploads.Add(upload.id, upload);

                context.Response.StatusCode = 400;
                context.Response.StatusDescription = "Bad Request";
                context.Response.End();


            }

            string boundary = Nancy.HttpMultipart.ExtractBoundary( contentType );

            upload.id = uploadId;
            upload.status = 0;
            Uploads.Add(upload.id, upload);


            try {

                if (wr.HasEntityBody())
                {
                    upload.bytesRemaining = 
                    upload.bytesTotal     = wr.GetTotalEntityBodyLength();

                    upload.bytesLoaded    = 
                    upload.BytesReceived  = wr.GetPreloadedEntityBodyLength();

                    if (!wr.IsEntireEntityBodyIsPreloaded())
                    {
                        byte[] buffer = new byte[KB * 8];
                        int readSize = buffer.Length;

                        ms = new MemoryStream();
                        //fs = new FileStream(context.Server.MapPath(ConfigurationManager.AppSettings["HttpUploadModule.Folder"].TrimEnd('/')+'/' + uploadId.ToString()), FileMode.CreateNew);

                        while (upload.bytesRemaining > 0)
                        {
                            upload.BytesReceived = wr.ReadEntityBody(buffer, 0, readSize);

                            if(upload.bytesRemaining == upload.bytesTotal) {

                            }

                            ms.Write(buffer, 0, upload.BytesReceived);

                            upload.bytesLoaded += upload.BytesReceived;
                            upload.bytesRemaining -= upload.BytesReceived;

                            if (readSize > upload.bytesRemaining)
                            {
                                readSize = upload.bytesRemaining;
                            }

                        }

                        //fs.Flush();
                        //fs.Close();
                        ms.Position = 0;
                        //the file is in our hands
                        Nancy.HttpMultipart multipart = new Nancy.HttpMultipart(ms, boundary);
                        foreach( Nancy.HttpMultipartBoundary b in multipart.GetBoundaries()) {
                            if(b.Name == "data")   {

                                upload.filename = uploadId.ToString()+Path.GetExtension( b.Filename ).ToLower();

                                fs = new FileStream(context.Server.MapPath(ConfigurationManager.AppSettings["HttpUploadModule.Folder"].TrimEnd('/')+'/' + upload.filename  ), FileMode.CreateNew);
                                b.Value.CopyTo(fs);
                                fs.Flush();
                                fs.Close();

                                upload.status = 1;

                                context.Response.StatusCode = 200;
                                context.Response.StatusDescription = "OK";
                                context.Response.Write(  context.Request.ApplicationPath.TrimEnd('/') + "/images/temp/" +  upload.filename  );
                            }
                        }

                    }

                }
            }
            catch(Exception ex) {
                upload.ex = ex;
            }

            if(upload.status != 1)
            {
                upload.status = 2;
                context.Response.StatusCode = 400;
                context.Response.StatusDescription = "Bad Request";
            }
            context.Response.End();
        }
    }
}

public class UploadData {
    public Guid id { get;set; }
    public string filename {get;set;}
    public int bytesLoaded { get; set; }
    public int bytesTotal { get; set; }
    public int BytesReceived {get; set;}
    public int bytesRemaining { get;set; }
    public int status { get;set; }
    public Exception ex { get;set; }
    public DateTime createdDate { get;set; }
} 
于 2013-07-21T03:28:02.967 回答