2013-03-19 4 views
13

Wie kann ich eine große Datei mit ASP.NET MVC4 Web Api
hochladen und auch einen Fortschritt bekommen?Wie lade ich eine große Datei mit ASP.NET MVC4 Web API mit Fortschrittsbalken

Ich habe diesen Post gesehen und ich verstehe, wie man mit der hochgeladenen Datei umgeht, aber wie kann ich die Fortschrittsdaten bekommen? How To Accept a File POST

Bitte senden Sie mir keine Links zum Hochladen von Produkten. ich möchte zu verstehen, wie dies in der Art und Weise MVC4 Web Api Griff ... hier ist ein Beispielcode

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")); 
     } 
    } 

jetzt, wenn

await Request.Content.ReadAsMultipartAsync(provider) 

wie kann eine Datei-Upload in MVC4 WebAPI Handhabung i Wie werden Bytes geladen?

Antwort

20

Es gibt eine Beschränkung für die Größe von Dateien, die standardmäßig an zwei Stellen hochgeladen werden. Ein auf der Anforderungsebene und zweitens, wenn Sie auf IIS hosten, dann auf Webserverebene. Ich fügte einige Konfigurationen hinzu, wie in this blog erwähnt, und ich konnte eine 36mb-Datei ohne irgendwelche Probleme hochladen. Ich habe das folgende Snippet gepostet.

Grundsätzlich

1.

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

2.

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

Es ist einfach, die Größe der Datei in den Server geladen zu finden, wenn Sie es wünschen. In Ihrem Code

beim Lesen durch die Filedata im Stream, für jedes Element in Ihrer Datei Daten, können Sie den lokalen Dateinamen wie unten gezeigt lesen.

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 

Hoffe, das hilft. Ich frage mich, wieso das abgesagt wurde?

+5

danke für die nützlichen info, ich weiß bereits über das upload limit, dass nicht das, was ich frage, ich kann keinen weg finden, um den fortschritt der hochgeladenen bytes, meine aktion werden aufgerufen, nachdem alle anfrage körper war hochgeladen. – ygaradon

+1

Das war nicht was OP gefragt – Learner

0

Ich endete mit einem HttpModule aber auch das HttpModule wird nicht die Fortschrittsbalken Ich fand etwas sehr interessant es scheint, dass wenn ich die Datei in einem sicheren Protokoll (über https: //) hochladen dann den Fortschritt funktionieren, aber in nicht sicheren Protokoll (http: //) der Fortschritt funktioniert nicht und die Datei ist vollständig gepuffert Ich weiß nicht, wie es ist, dass ich glaube, es ist ein Fehler irgendwo zwischen dem IIS zu Asp.net Framework, wenn die Anfrage sind Proced bekommen.

jetzt, weil ich Erfolg es über https mit einem HttpModule arbeiten lasse ich glaube, dass es möglich ist, es mit Mvc Web Api arbeiten zu lassen, aber ich habe zur Zeit nicht die Zeit, das zu überprüfen.

für Mutlipart Formulardaten Parsen verwendet i Nancy HttpMultipart Parser hier: https://github.com/NancyFx/Nancy/tree/master/src/Nancy packte nur die Klassen:
HttpMultipart.cs
HttpMultipartBoundary.cs
HttpMultipartBuffer.cs
HttpMultipartSubStream.cs

hier ist die Httpmodule Quelle:

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

Ich benutze diese Lösung:

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

und kleine configure für ungepufferten Eingangsstrom:

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

implementiert diese:

public class CustomPolicy : System.Web.Http.WebHost.WebHostBufferPolicySelector 
{ 
    public override bool UseBufferedInputStream(object hostContext) 
    { 
     return false; 
    } 
} 
+0

sehen interessant aus ... Ich muss Zeit finden, um es zu testen .. – ygaradon

Verwandte Themen