Remote file download using .NET - c#

I'm trying to download remote file using C# (ASP.NET). The problem is when I browse to the file download URL - it downloads perfectly. When I try the WebClient.DownloadData(url) I get "no data to show" response.
If I browse using the built-in VS2010 browser I still get this "error" message.
The file link is: http://www.tase.co.il/TASE/Pages/Export.aspx?sn=he-IL_ds&enumTblType=AllSecurities&Columns=he-IL_Columns&Titles=he-IL_Titles&TblId=0&ExportType=3
(CSV file)
How can the file be downloaded? any ideas?
Many Thanks

This isn't a download problem on your side, that page is referencing something, probably from session...so you would need to login to the site, get a session, basically go through whatever steps you normally would in route to getting the file so it's generated correctly/available.
If I had to take a guess, I'm thinking they do the export to the file system when you ask for it, so it's a temp file somewhere...not something you can just grab, so unless you went through the step that created it (doesn't seem this Export.aspx page does it), it won't be there, and their error message for this is "No data to show".
There's also the possibility they're explicitly blocking you to prevent any kind of bots/leeching from happening...basically preventing exactly what you're trying to do.

Many sites will block downloads based on the Referer or User-Agent header.
Watch a "working" download with
Fiddler
Watch your code's
download with Fiddler
Compare the
two requests
Update your request
to match the "working" case.

I solved the problem by obtaining the remote file as a stream. From that point I was able to read/write the stream to the response.
//The absolute path to your file
string remoteFile = "http://my.cloudfront.net/videos/vehicle_english_v3.mp4";
//The name of the file you want the user to see when they download the file
int pos = remoteFile.LastIndexOf("/") + 1;
string fileName = remoteFile.Substring(pos, remoteFile.Length - pos);
//Obtain your file as a stream
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(remoteFile);
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
Stream fileStream = response.GetResponseStream();
//Write the stream to the response
if (fileStream != null && fileStream.CanRead) {
context.Response.AddHeader("Content-Disposition", "attachment; filename=\"" + fileName + "\"");
context.Response.ContentType = "application/octet-stream";
context.Response.ClearContent();
fileStream.CopyTo(context.Response.OutputStream);
}

Related

BitmapFrame - No imaging component suitable to complete this operation was found [duplicate]

I'm downloading in image from web to save it locally. It works great with any other image formats but it this method below fails with an argument exception when I try to read a WebP image.
private static Image GetImage(string url)
{
try
{
HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(url);
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
return Image.FromStream(response.GetResponseStream());
}
catch
{
return null;
}
}
How do you read .webp images in C#?
I found this other question that allows for converting between types but I do not want to do that WebP library for C#
Reason I'm not wanting to do it is because I think it might lose some quality. Besides, I want to know why is this not working.
The base class libraries won't help you to deal with WebP images. However, if you only want to save the received file to the disk, you don't have to even know that you are dealing with a WebP images. You can simply treat the received data as a binary blob and dump it to a file, for example using Stream.CopyTo and a FileStream.
The Content-Type HTTP header will give you the mime type of the file you're downloading, and the Content-Disposition header can provide you with a filename and extension (though you might have to do some parsing). You can access those using HttpWebResponse.ContentType and HttpWebResponse.Headers["Content-Disposition"].
#Trillian nailed it. Here is a code snippet for what I did based on his suggestion. Wanted to add code so not posting this as a comment.
To get just the image file extension, you can do this
HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(url);
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
string fileExt = response.ContentType.Replace("image/", string.Empty);
To get the file name with extension, you can do the following and the do parsing like I did above. It just has some more data in it.
response.Headers["Content-Disposition"];
Once you have you file name you want to save as, create a file stream and copy the response stream into it.
FileStream fs = new FileStream(targetPath + fileName, FileMode.Create);
response.GetResponseStream().CopyTo(fs);
Assuming you app has access to the destination, image should get saved. Make sure to add try catch and handle exceptions properly. Also note that FileMode.Create will overwrite if the file already exists!

Cannot Send File in Response

I have a code set that runs on the server, which correctly generates a zip file and stores it on the server. I have that file location as a physical path.
Nothing I have attempted has allowed me to use the response to the client to download that file.
Attempt 1:
System.IO.FileInfo fi = new System.IO.FileInfo(zipFilePath);
//setup HTML Download of provided Zip.
//application/zip
Response.ClearContent();
Response.Clear();
Response.ClearHeaders();
Response.Buffer = true;
Response.ContentType = "application / zip";
Response.AddHeader("Content-Disposition",
"attachment; filename=\"" + System.IO.Path.GetFileName(zipFilePath) + "\";");
Response.AddHeader("Content-Length", fi.Length.ToString());
Response.TransmitFile(zipFilePath);
Response.Flush();
Response.End();
No result. Code executes without error but there is no download to the client.
Attempt 2:
//Almost the same as attempt 1, but with WriteFile instead
Response.WriteFile(zipFilePath);
No Result, same as Attempt 1.
Attempt 3:
//Note: Same Header Section as Attempts 1 and 2
System.IO.BinaryReader reader = new System.IO.BinaryReader(new System.IO.FileStream(zipFilePath, System.IO.FileMode.Open));
int CHUNK = 1024;
List<byte> FileArray = new List<byte>();
while (reader.BaseStream.Position < reader.BaseStream.Length)
FileArray.AddRange(reader.ReadBytes(CHUNK));
byte[] bArray = FileArray.ToArray();
reader.Close();
Response.OutputStream.Write(bArray, 0, bArray.Length);
Response.Flush();
Response.End();
No Result, Same as previous attempts
Attempt 4:
//Identical to Attempt 3, but using BinaryWrite
Response.BinaryWrite(bArray);
No Result, Same as previous Attempts.
The Question
Every one of these code blocks runs with no error, But The Save File dialog NEVER appears. I get nothing at all. I cannot figure out for the life of me what I might be missing.
The File Path has been verified as correct
The Code is running on the server, not on the client, I cannot use the 'WebClient.Download' method for this reason
If anyone has any suggestions, I'm all ears. I have no idea how to get this file to download to the client.
I tested your code (attempt 1) and got it working fine with a test file. If the file path would be wrong, you'd get an System.IO.FileNotFoundException so that's probably not the issue.
A couple of ways to address this:
Try inspecting the webpage in, for example, Chrome by right-clicking
and choose inspect. Then click on Network tab, and refresh the
page (where you're supposed to get the file). Check the response
headers for that request - what is it?
Try setting content-type to application/octet-stream
Use debugger in Visual Studio and step through.
This turned out to be an Ajax related error causing issues between UpdatePanels and POST Responses.
The issue was fixed on the page load of the page by adding the call
ScriptManager.GetCurrent(Page).RegisterPostBackControl(btnGenerate);

Downloading a file, can you cancel the response when an error occurs, without buffering the content

I am writing large CSV files to the Response with Response.BufferOutput set to false. This is because the file has the potential to take a long time to download so the user can see some progress. I generate a line for the CSV from a object and write to the response using Response.Write().
This works well however if there is an unexpected error after the Response has started writing then the client will receive a file with only part of the data and could be missing lots of lines but they might not realise it.
Is there a way to somehow cancel the file download without buffering all the content? Could there be some way to indicate that the response is invalid so the browser disregards the file?
Code below shows the main idea of my code
public void StreamCsvFile(string fileName,List<myObject> myObjectList)
{
Response.Clear();
Response.ContentType = "text/csv";
Response.AddHeader("content-disposition", "filename=" + fileName);
Response.BufferOutput = false;
string headerLine = GetHeaderLine();
Response.Write(headerLine)
try
{
foreach(var myObject in myObjectList)
{
string line = myObject.ToCsvString();
Response.Write("\n" + line);
}
}
finally
{
Response.End();
}
}
Write to a temporary file first.
Set the content-length on the response.
Use Response.TransmitFile to send this temp file.
Browsers will reject download if content length doesn't match.

Can you use "inline" content-disposition with "application/octet-stream"?

I need the browser to open file types it understands directly in the browser (i.e. no "Open/Save/Cancel" dialog.
Here's my code, which currently works great!...except that every file pops up the dialog box and doesn't directly open the file:
string filePath = Path.Combine(WebConfigurationManager.AppSettings["NewsAttachmentPath"], context.Request.QueryString["FileName"]);
byte[] bytes = System.IO.File.ReadAllBytes(filePath);
context.Response.Clear();
context.Response.ContentType = "application/octet-stream";
context.Response.Cache.SetCacheability(HttpCacheability.Private);
context.Response.Expires = -1;
context.Response.Buffer = true;
context.Response.AddHeader("Content-Disposition", string.Format("{0};FileName=\"{1}\"", "inline", context.Request.QueryString["FileName"]));
context.Response.BinaryWrite(bytes);
context.Response.End();
As you can see, even when I change the Content-Disposition to "inline", it still prompts for download. This is with files that I know my browser understands. In other words, I can go to some random site and click a PDF, and it will open in the browser. My site will make me save it in order to view it.
Pre-emptive answer to "why do you wanna use application/octet-stream?" Because I don't want to create a handler for each single file type. If this is mistaken, please let me know.
You do not need to create a handler per file type. You just change the line:
context.Response.ContentType = "application/octet-stream";
to be:
string contentType = //your logic here, possibly many lines in a separate method
context.Response.ContentType = contentType;
But no: you can't "inline" an application/octet-stream. That means "here's some bytes, but I don't know what they are". The browser can't do much with that, other than save it somewhere, hence a download prompt. You can use content-disposition to suggest a filename, though.
The browser does not work on file extensions - it works on content-type. So: you need to report the correct content-type in your response. This might mean writing a switch / lookup based on the file extension that you know, or it might mean storing the explicit content-type separately as metadata along with the file information.

C# downloading multiples files from the server side through the browser

Anyone can help me on downloading multiple files from the server side through the browser (i.e. Chrome) I've debbuged it the foreach iterates fine but it only downloads the first file. Any suggestions would be greatly appreciated cheers since I'm getting way frustrated.
Here's the code snippet:
FileInfo fInfo;
SQLConnection = new SqlConnection(SQLConnectionString);
foreach (CartItem CartProduct in Cart.Instance.Items)
{
SQLCom = new SqlCommand("spSelect_T", SQLConnection);
SQLCom.CommandType = CommandType.StoredProcedure;
SQLCom.Parameters.Add(new SqlParameter("#id", CartProduct.ProductId));
SQLConnection.Open();
SQLDReader = SQLCom.ExecuteReader();
if (SQLDReader.Read() == true)
{
fInfo = new FileInfo(Server.MapPath(SQLDReader["t_path"].ToString()));
Response.Clear();
Response.Expires = -1;
Response.AddHeader("Content-Disposition", "attachment; filename=\"" + fInfo.Name + "\"");
Response.AddHeader("Content-Length", fInfo.Length.ToString());
Response.ContentType = "application/octet-stream";
Response.WriteFile(fInfo.FullName);
Response.End();
}
SQLConnection.Close();
}
Each request can only have one response. I recommend you zip up the files and send them back in one package.
Scriptworks, you're fighting against the way the protocol works. Doesn't matter whether you use ASP/PHP, or any other web framework. You can only send one set of headers and a body stream back for any given request. Your options are limited.
With respect to files this means you can transfer only one file per HTTP request. If other suggestions like Zipping to create a single response file don't work, your only alternative is to get the client to make multiple requests.
Easiest is probably doing this in AJAX, or hidden IFRAMEs.
First you're going to need to either have a handler that takes a filename and returns it's headers and filestream, or a static file web server with the files in it's content directory.
In the case of AJAX, you'd send a client-script the list of file URLs, so it can in script iterate through the list, requesting each URL in turn.
The latter, using server or script to generate IFRAMEs whose src attribute is set to the URLs of your files.
Neither of these solutions are "pretty" user experiences.
I would guess because you are calling Response.End() inside the loop. After the first iteration it prevents anything else going back to the client.
Why don't you zip your files together into a single download? There are good, open source C# compression libraries that make this simple.

Categories

Resources