c# - Web API - Get progress when uploading to Azure storage -
the task want accomplish create web api service in order upload file azure storage. @ same time, have progress indicator reflects actual upload progress. after research , studying found out 2 important things:
first have split file manually chunks, , upload them asynchronously using putblockasync method microsoft.windowsazure.storage.dll.
second, have receive file in web api service in streamed mode , not in buffered mode.
so until have following implementation:
uploadcontroller.cs
using system.configuration; using system.net; using system.net.http; using system.threading.tasks; using system.web.http; using microsoft.windowsazure.storage; using microsoft.windowsazure.storage.blob; using webapifileuploadtoazurestorage.infrastructure; using webapifileuploadtoazurestorage.models; namespace webapifileuploadtoazurestorage.controllers { public class uploadcontroller : apicontroller { [httppost] public async task<httpresponsemessage> uploadfile() { if (!request.content.ismimemultipartcontent("form-data")) { return request.createresponse(httpstatuscode.unsupportedmediatype, new uploadstatus(null, false, "no form data found on request.", string.empty, string.empty)); } var streamprovider = new multipartazureblobstorageprovider(getazurestoragecontainer()); var result = await request.content.readasmultipartasync(streamprovider); if (result.filedata.count < 1) { return request.createresponse(httpstatuscode.badrequest, new uploadstatus(null, false, "no files uploaded.", string.empty, string.empty)); } return request.createresponse(httpstatuscode.ok); } private static cloudblobcontainer getazurestoragecontainer() { var storageconnectionstring = configurationmanager.appsettings["azureblobstorageconnectionstring"]; var storageaccount = cloudstorageaccount.parse(storageconnectionstring); var blobclient = storageaccount.createcloudblobclient(); blobclient.defaultrequestoptions.singleblobuploadthresholdinbytes = 1024 * 1024; var container = blobclient.getcontainerreference("photos"); if (container.exists()) { return container; } container.create(); container.setpermissions(new blobcontainerpermissions { publicaccess = blobcontainerpublicaccesstype.container }); return container; } } }
multipartazureblobstorageprovider.cs
using system; using system.collections.generic; using system.diagnostics; using system.io; using system.linq; using system.net.http; using system.text; using system.threading; using system.threading.tasks; using microsoft.windowsazure.storage.blob; namespace webapifileuploadtoazurestorage.infrastructure { public class multipartazureblobstorageprovider : multipartformdatastreamprovider { private readonly cloudblobcontainer _blobcontainer; public multipartazureblobstorageprovider(cloudblobcontainer blobcontainer) : base(path.gettemppath()) { _blobcontainer = blobcontainer; } public override task executepostprocessingasync() { const int blocksize = 256 * 1024; var filedata = filedata.first(); var filename = path.getfilename(filedata.headers.contentdisposition.filename.trim('"')); var blob = _blobcontainer.getblockblobreference(filename); var bytestoupload = (new fileinfo(filedata.localfilename)).length; var filesize = bytestoupload; blob.properties.contenttype = filedata.headers.contenttype.mediatype; blob.streamwritesizeinbytes = blocksize; if (bytestoupload < blocksize) { var cancellationtoken = new cancellationtoken(); using (var filestream = new filestream(filedata.localfilename, filemode.open, fileaccess.readwrite)) { var upload = blob.uploadfromstreamasync(filestream, cancellationtoken); debug.writeline($"status {upload.status}."); upload.continuewith(task => { debug.writeline($"status {task.status}."); debug.writeline("upload on successfully."); }, taskcontinuationoptions.onlyonrantocompletion); upload.continuewith(task => { debug.writeline($"status {task.status}."); if (task.exception != null) { debug.writeline("task not completed." + task.exception.innerexception); } }, taskcontinuationoptions.onlyonfaulted); upload.wait(cancellationtoken); } } else { var blockids = new list<string>(); var index = 1; long startposition = 0; long bytesuploaded = 0; { var bytestoread = math.min(blocksize, bytestoupload); var blobcontents = new byte[bytestoread]; using (var filestream = new filestream(filedata.localfilename, filemode.open)) { filestream.position = startposition; filestream.read(blobcontents, 0, (int)bytestoread); } var manualresetevent = new manualresetevent(false); var blockid = convert.tobase64string(encoding.utf8.getbytes(index.tostring("d6"))); debug.writeline($"now uploading block # {index.tostring("d6")}"); blockids.add(blockid); var upload = blob.putblockasync(blockid, new memorystream(blobcontents), null); upload.continuewith(task => { bytesuploaded += bytestoread; bytestoupload -= bytestoread; startposition += bytestoread; index++; var percentcomplete = (double)bytesuploaded / filesize; debug.writeline($"percent complete: {percentcomplete.tostring("p")}"); manualresetevent.set(); }); manualresetevent.waitone(); } while (bytestoupload > 0); debug.writeline("now committing block list."); var putblocklist = blob.putblocklistasync(blockids); putblocklist.continuewith(task => { debug.writeline("blob uploaded completely."); }); putblocklist.wait(); } file.delete(filedata.localfilename); return base.executepostprocessingasync(); } } }
i enabled streamed mode this blog post suggests. approach works great, in terms file uploaded azure storage. then, when make call service making use of xmlhttprequest (and subscribing progress event) see indicator moving 100% quickly. if 5mb file needs around 1 minute upload, indicator moves end in 1 second. problem resides in way server informs client upload progress. thoughts this? thank you.
================================ update 1 ===================================
that javascript code use call service
function uploadfile(file, index, uploadcompleted) { var authdata = localstorageservice.get("authorizationdata"); var xhr = new xmlhttprequest(); xhr.upload.addeventlistener("progress", function (event) { fileuploadpercent = math.floor((event.loaded / event.total) * 100); console.log(fileuploadpercent + " %"); }); xhr.onreadystatechange = function (event) { if (event.target.readystate === event.target.done) { if (event.target.status !== 200) { } else { var parsedresponse = json.parse(event.target.response); uploadcompleted(parsedresponse); } } }; xhr.open("post", uploadfileserviceurl, true); xhr.setrequestheader("authorization", "bearer " + authdata.token); var data = new formdata(); data.append("file-" + index, file); xhr.send(data); }
your progress indicator might moving rapidly fast, might because of
public async task<httpresponsemessage> uploadfile()
i have encountered before, when creating api of async type, im not sure if can awaited, of course finish api call on background, reason progress indicator instantly finish, because of async method (fire , forget). api give response, finish on server background (if not awaited).
please kindly try making
public httpresponsemessage uploadfile()
and try these ones
var result = request.content.readasmultipartasync(streamprovider).result; var upload = blob.uploadfromstreamasync(filestream, cancellationtoken).result;
or
var upload = await blob.uploadfromstreamasync(filestream, cancellationtoken);
hope helps.
Comments
Post a Comment