Download a large file with a c# webservice - c#

I am trying to create a webservice that would allow its consumers to download files (can be very huge files). At the server, I have many files that need to be sent back to the consumer, so I am compressing all those file into one big zip file and streaming them back to the user. Right now, my webservice will start compressing the files, when the request comes in, forms the zip files and streams it back.Sometimes compression can take a lot of time, and the request may time out. What can I do to avoid such situations? My solution right now is to, seperate the data into smaller zip files, send a response to consumer saying there would be these many smaller files, and let consumers send request for individual smaller files. So, if i have 1GB zip file, i will break it into 10 smaller zip files, and ask the consumer to request for smaller files in 10 requests. Is this the correct approach? What problems can I be facing? Has anyone dealth with such issues before? I would be glad if you can share your experiences. Also, is it possible to start streaming the zip files without forming them fully?

Treat the request and the delivery as asynchronous operations.
The client can make a request for the file using one method. Another method can let the client know the status of the file packaging (whether they are ready for download yet). A third method can actually download the files.

It may be worth looking at a restful approach. Rather than a soap web service. As OrbMan, suggested an asynch approach may be best.
With REST you could expose a resource as: http://yourlocation/generatefile
Which (when called with a post) returns a http response with a response code of 301 'accepted' and a location header value of location=http://yourlocation/generatefile/id00124 which suggests the location of the data.
You can then poll the http://yourlocation/generatefile/id00124 resource (maybe just header request) to get the status i.e. processing / complete.
When processing is complete. Do a get on the http://yourlocation/generatefile/id00124 to download your file. The response http message should identify you file and the format i.e. encryption and compression types so any consumer knows how to read it.
This is a nice solution to problems which are long running and returns data in formats other than soap anbd general xml.
I hope this helps

I would poll from the calling client as part of the method which gets the file. The client code might flow something like this:
byte[] GetFile()
{
response = request.Post(http://yourlocation/generatefile);
string dataResource = response.Headers["Location"];
bool resourceReady = false;
while(!reasourceReady)
{
resH = request.Header(dataResource);
if(resH.Headers[Status] == "complete")
break;
else
Thread.Sleep(OneSecond); ?? or whetever
}
fileRes = request.Get(dataResource);
return fileRes.ToByteArray();
}
This is only psuedo, but I hope it makes sense...

Related

Is it possible to access parameters sent via multipart/form-data without waiting for the entire form to arrive

I have a pretty big video file I upload to a web service via multipart/form-data.
It takes ~ 30 seconds to arrive and I would prefer not waiting that long simply to access parameters I send along with the file.
My question is simple, can I access parameters sent with the form without waiting for the video payload to be uploaded?
Can this be done using headers or any other methods? 
Streaming vs. Buffering
It's about how the webserver is set up. For IIS you can enable Streaming.
Otherwise, by default, IIS will use 'buffering' - the whole request is loaded into memory first (IIS's memory that you can't get to) before your app running in IIS can get it.
Not using IIS? You have to figure out how to get the webserver to do the same thing.
How to stream using IIS:
Streaming large file uploads to ASP.NET MVC
Note the way the file is read in the inner loop:
while ((cbRead = clientRequest.InputStream.Read(rgbBody, 0, rgbBody.Length)) > 0)
{
fileStream.Write(rgbBody, 0, cbRead);
}
Here instead of just saving the data like that question does, you will have to parse any xml/json/etc or whatever contains the file parameters you speak of ... and expect the video to be sent afterwards. You can process them right away if it's a quick process ... then get the rest of the video ... or you can send them to a background thread.
You probably won't be able to parse it just dumping what you have to a json or xml parser, there will be an unclosed tag or } at the top that isn't closed til after the video data is uploaded (however that is done). Or if it's multipart data from a form submission, as you imply, you will have to parse that partial upload yourself, instead of just asking IIS for the post data.
So this will be tricky, you can first start by writing 1k at a time to a log file with a time stamp to prove that you're getting the data as it comes. after that it's just a coding headache.
Getting this to work also means you'll have to have some control over the client and how it sends the data.
That's because you'll at least have to ensure it sends the file parameters FIRST!
Which concerns me, because, if you have control of the client, why can't you take the simple route (as Nobody and Nkosi imply) and use 2 requests? You mention you need one. Why not write js client code to send the parameters first in an XHR and then the file in a second request, using a correlation ID in both to tie them together? (the server could return this from the first request and you could send it in the 2nd).
Obviously, if you're just having a form with some inputs and a file upload and doing submit, then you need one request ;-) But if you have control over the client side you're not stuck with that.
Good luck, there is some advanced programming here, but nothing super high-tech. You will make it work!!!
If you don't have control over the server code, you are probably stuck, if the server app's webserver is buffering, the server app won't get anything, of course, if you wanted to do something with the file parameters first, this really implies you have control of the server side ;-)

WebClient doing something weird with large file

I have a Varnish server in front of a S3 bucket. An API will generate a private URL and allow me to download private files of this bucket through the Varnish server.
Whenever I download a 500MB file directly from the bucket or through the Varnish-server in Chrome, everything works fine.
When I move the same logic to a C# WebClient (with proxy set to NULL and only sending an User Agent header), directly downloading from the bucket works fine. When I change the URL to the Varnish-server, things start to topple over... It will stop receiving the file at exactly 104.640KB every single time.
I'll get an IOException the Stream ended unexpectedly. I've tried both DownloadData, DownloadFile and their Async counterparts. It simply will never finish the download.
I've went back and forth from .NET 2.0 all the way to 4.5.1. Does anyone have a clue why this happens?
I came to the conclusion Varnish does some really weird things with its headers. I eventually setup OpenResty (nginx+lua) and used S3CMD to sync files whenever an URL is requested in a certain way, and used LUA to make a similar system to pre-signed URL's. This turned out to work perfectly in combination with the C# WebClient, and on top of that, I can now actually see that files really come from this particular server, which is not the case if Varnish caches it.

I have httpwebrequest object, want to upload it in parts

I have below code in a third party function, it posts file to a webserver. I want to post data in parts, what change should i do in the code. Below code is working and the "request" object contains everything.
private static HttpWebResponse GetRawResponse(HttpWebRequest request)
{
return (HttpWebResponse)request.GetResponse();
}
Also is there a way in which I can find out what is the full name (with path) of the file which is going to be uploaded from the httpwebrequest object.
Thanks.
HttpWebRequest does not spawn multiple HTTP requests. You cannot upload a file in chunks unless you actually create multiple HttpWebRequests. I don't think that would be useful unless you have a server side process to stitch them back together.
If you still want true chunked uploading you might want to look at raw TCP or some other mechanism.
Hope that helps. See this post as well.

Best way to transfer files through a web service

My C# program communicates with a server using a web service, I need the client to download big files from the server and have the option to pause and continue their download, the downloader must also be authorized to download the file.
I had two thoughts on how to do that,
one is to use some 3rd party API like wget to download the files. the problem with that is that I need to learn the API commands and that I'm not certain I can show my download progress in the program, another issue is that I would have to use use bare URLs to get the files from the server which seems ugly and could lead to people just downloading them off the server (I want them to be authorized, although this isn't a real issue since this is just a school project).
My other thought was to create a method on the web service that will get a position in the file and an amount of bytes and return them and the client will piece them together, it seems more complicated but more compelling since the user must be authorized to download the file and I can use it to show the tester some more advanced programming skills ;). The issue with that looks like it might be performance taxing.
What's your opinion? what's the best way to download big files off a server?
Absent the need for authorization and partial downloads, WebClient.DownloadData or WebClient.DownloadDataAsync would be the preferred method of downloading a file from a server.
You could still use WebClient for the authorization by setting the Credentials in your WebClient object instance. If the user isn't authorized to download the file, based on those credentials, the server can return a 404 (Not found) or 403 (Forbidden).
If your server supports HTTP 1.1, the client can start in the middle of the file. To do so, you'll have to create a class that inherits from WebClient and override the GetWebRequest method. That method would then set the headers to do a positional GET.
class MyWebClient : WebClient
{
public int StartDownloadAt { get; set; }
protected override WebRequest GetWebRequest(Uri address)
{
HttpWebRequest req = (HttpWebRequest)base.GetWebRequest(address);
req.AddRange(position_to_start);
}
}
And in the code that uses it:
MyWebClient client = new MyWebClient();
client.StartDownloadAt = 1024 * 2024; // start download 1 megabyte into file.
client.DownloadData(...);
The above is just an example. You'd probably want to make that more robust by having the StartDownLoadAt property reset to 0 when a download is done (or aborted), and not do the AddRange if StartdownloadAt is set to 0. To fully support ranges, you'd probably want properties for start and end range, etc.
And, of course, the client will have to handle stitching the disparate downloaded pieces together after download is complete.
The point is that it should be possible, with a little work, by using the WebClient class.

ASMX file upload

Is there a way to upload a file from local filesystem to a folder in a server using ASMX web services(no WCF, don't ask why:)?
UPD
P.S.file size can be 2-10 GB
Sure:
[WebMethod]
public void Upload(byte[] contents, string filename)
{
var appData = Server.MapPath("~/App_Data");
var file = Path.Combine(appData, Path.GetFileName(filename));
File.WriteAllBytes(file, contents);
}
then expose the service, generate a client proxy from the WSDL, invoke, standard stuff.
--
UPDATE:
I see your update now about handling large files. The MTOM protocol with streaming which is built into WCF is optimized for handling such scenarios.
When developing my free tool to upload large files to a server, I am also using .NET 2.0 and web services.
To make the application more error tolerant for very large files, I decided to not upload one large byte[] array but instead do a "chuncked" upload.
I.e. for uploading a 1 MB file, I do call my upload SOAP function 20 times, each call passing a byte[] array of 50 KB and concating it on the server together again.
I also count the packages, when one drops, I try to upload it again for several times.
This makes the upload more error tolerant and more responsive in the UI.
If you are interested, this is a CP article of the tool.
For very large files, the only efficient way to send them to web services is with MTOM. And MTOM is only supported in WCF, which you have ruled out. The only way to do this with old-style .asmx web services is the answer that #Darin Dimitrov gave. And with that solution, you'll have to suffer the cost of the file being base64 encoded (33% more bandwidth).
We had the same requirement, basically uploading a file via HTTP POST using the standard FileUpload controls on the client side.
In the end we just added an ASPX page to the ASMX web service project (after all its just a web project) - this allowed us to upload to i.e. http://foo/bar/Upload.aspx when the web service was at http://foo/bar/baz.asmx. This kept the functionality within the web service, even though it was using a separate web page.
This might or might not fit your requirements, #Darins approach would work as a workaround as well but you would have to make modifications on the client side for that, which wasn't an option for us.
You can try to convert the file to Base64 and pass it as a string to the service and then convert back to a byte array.
https://forums.asp.net/t/1980031.aspx?Web+Service+method+with+Byte+array+parameter+throws+ArgumentException
How to convert file to base64 in JavaScript?
The input is not a valid Base-64 string as it contains a non-base 64 character

Categories