2013-03-19 55 views
13

如何上传带有ASP.NET MVC4 Web Api的大文件
并获得进度?如何上传带有ASP.NET MVC4 Web Api的大文件与进度条

我看到这篇文章,我明白如何处理上传的文件,但我如何获得进度数据? How To Accept a File POST

请不要给我发送上传产品的链接。 我想了解如何在MVC4网页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) 

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

回答

20

默认情况下在两个地方上传文件的大小有限制。一个在请求级别,其次,如果你在IIS上托管,那么在Web服务器级别上。我添加了this blog中提到的几个配置,并且我能够无任何问题地上传36mb文件。我已经发布了下面的代码片段。

基本上

1.

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

2.

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

它很容易找到加载到服务器,如果你希望文件的大小。在您的代码

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

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 

希望这会有所帮助。我想知道为什么这个标记了?

+5

感谢您的有用信息,我已经意识到上传限制,而不是我所要求的,我无法找到一种方法来获取上传字节的进度,我的行动被调用后,所有的请求机构是上传。 – ygaradon 2013-03-27 14:45:13

+1

这不是什么OP问 – Learner 2015-07-16 20:43:46

0

我结束了使用HttpModule,但即使HttpModule不会显示进度条 我发现了一些非常有趣的事情似乎,当我上传文件在安全协议(通过https://),然后进度正在工作,但在不安全的protocl(http://)的进展是行不通的,文件被完全缓冲,我不知道方式是这样的,我相信这是一个错误之间的IIS到Asp.net框架之间的请求被获取。

现在,因为我成功使它通过https与HttpModule工作,我相信它有可能使其与Mvc Web Api工作,但我目前没有时间来检查。

解析Mutlipart表单数据,我用南希HttpMultipart解析器这里: https://github.com/NancyFx/Nancy/tree/master/src/Nancy 抓了类:
HttpMultipart.cs
HttpMultipartBoundary.cs
HttpMultipartBuffer.cs
HttpMultipartSubStream。CS

这里是HTTP模块来源:

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; } 
} 
3

我使用此解决方案:

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; 
    } 
} 
+0

看起来有趣...我需要找时间来测试它.. – ygaradon 2014-03-26 15:50:57