Как загрузить большой файл с ASP.NET MVC4 Web Api с 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)

как я могу получить загрузку байтов?

Ответ 1

Существует ограничение на размер файлов, которые будут загружаться по умолчанию в двух местах. Один на уровне запроса, а во-вторых, если вы размещаете на IIS, то на уровне веб-сервера. Я добавил пару конфигураций, упомянутых в в этом блоге, и я смог загрузить файл 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

Надеюсь, это поможет. Интересно, почему это было отмечено?

Ответ 2

Я использую это решение:

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

Ответ 3

Я закончил работу с использованием HttpModule, но даже HttpModule не покажет индикатор выполнения Я узнал что-то очень интересное, кажется, что когда я загружаю файл в защищенный протокол (через https://), то прогресс работает, но в небезопасном protocl (http://) прогресс не работает, и файл полностью буферизованный, я не знаю, как это похоже, я считаю, что ошибка где-то между IIS и Asp.net Framework, когда запрос получает Procced.

теперь, потому что я добился успеха в работе над 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; }
}