簡體   English   中英

如何使用帶有進度條的ASP.NET MVC4 Web Api上傳大文件

[英]how to upload a large file with ASP.NET MVC4 Web Api with progressbar

如何使用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)

我怎樣才能得到字節加載的方式?

默認情況下,在兩個位置上傳文件的大小有限制。 一個在請求級別,第二個,如果您在IIS上托管,那么在Web服務器級別。 我添加了幾個配置,如本博客中所述 ,我能夠上傳一個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

希望這可以幫助。 我想知道為什么這會被標記下來?

我使用這個解決方案:

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

我使用HttpModule結束了,但即使是HttpModule也不會顯示進度條我發現了一些非常有趣的內容似乎當我在安全協議中上傳文件時(通過https://)然后進度正在運行但非安全protocl(http://)進度不起作用,文件是完全緩沖的我不知道是什么樣的,我相信這是IIS到Asp.net框架之間的一個錯誤,當請求被獲取時。

現在因為我成功通過https與HttpModule一起工作我相信它可以使它與Mvc Web Api一起工作,但我目前沒有時間檢查它。

為了解析Mutlipart表單數據,我在這里使用了Nancy HttpMultipart解析器: https//github.com/NancyFx/Nancy/tree/master/src/Nancy剛剛抓住了這些類:
HttpMultipart.cs
HttpMultipartBoundary.cs
HttpMultipartBuffer.cs
HttpMultipartSubStream.cs

這是HttpModule來源:

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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM