Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

how can i upload a large file with ASP.NET MVC4 Web Api
and also get a progress?

i saw this post and i understand how to handle the uploaded file but how i can get the progress data? How To Accept a File POST

please don't send me links to upload products. i want to understand how handle this in the MVC4 Web Api way... here is an example code of handling a file upload in 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"));
        }
    }

now when

   await Request.Content.ReadAsMultipartAsync(provider)

how can i get how bytes loaded?

like image 296
ygaradon Avatar asked Mar 19 '13 17:03

ygaradon


2 Answers

There is a limitation to the size of files to be uploaded by default at two places. One at the request level, and second , if you hosting on IIS, then on web server level. I added couple of configs as mentioned in this blog, and i was able to upload a 36mb file without any issues. I have posted the snippet below.

Basically

1.

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

2.

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

Its easy to find the size of the file loaded into the server if you wish. In your code

while reading through the filedata in the stream, for each item in you file data, you can read the local file name as shown below.

 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

Hope this helps. I wonder why this was marked down?

like image 185
Mridul Avatar answered Oct 05 '22 09:10

Mridul


I use this solution:

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

and small configure for non-buffered input stream:

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

implemented this:

public class CustomPolicy : System.Web.Http.WebHost.WebHostBufferPolicySelector
{
    public override bool UseBufferedInputStream(object hostContext)
    {
        return false;
    }
}
like image 41
Nik Avatar answered Oct 05 '22 08:10

Nik