Cannot Send File in Response - c#

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

Related

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.

Flushing stream of ZIP

I'm trying to Flush a Zip file that has just been created, and have an issue with it:
a) it doesn't actually Flush (I used the code before with the CSV files and all works well)
Here is the code:
outputMemStream.Position = 0;
System.Web.HttpResponse Resp = System.Web.HttpContext.Current.Response;
Resp.ClearContent();
Resp.ClearHeaders();
Resp.ContentType = "application/zip";
Resp.AddHeader("Content-Disposition", "attachment; filename=\"download.zip\"");
//byte[] buff = outputMemStream.ToArray();
//File.WriteAllBytes(path, buff);
outputMemStream.CopyTo(Resp.OutputStream);
outputMemStream.Close();
outputMemStream.Dispose();
Resp.Flush();
Resp.End();
The stream is created correctly because I am able to save it and can check it later that it looks alright.. just that thing that my browser doesn't allow me to download it is quite annoying.
Cheers for your help!
it's in the aspx page (in the code behind .cs), in one of the method
that happens after pressing button "Export All"
This is probably the issue. The page is rendered both your file and your controls (including your "Export All" button).
Try moving the logic for downloading to a different page or handler that does nothing but return the file.

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.

Remote file download using .NET

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

Download a string as a file

When the user clicks a button, I want to build a string, then have the user download that string as a file (it's a CSV file).
var response = HttpContext.Current.Response;
response.ClearContent();
response.Clear();
byte[] bytes = Encoding.ASCII.GetBytes(csvtext);
using (var stream = new MemoryStream(bytes))
{
response.AddHeader("Content-Disposition", "attachment; filename=somefile.csv");
response.AddHeader("Content-Length", stream.Length.ToString());
response.ContentType = "text/plain";
stream.WriteTo(response.OutputStream);
}
This is what I have so far, and I have a feeling I'm pretty close, but I get the following error message:
Microsoft JScript runtime error: Sys.WebForms.PageRequestManagerParserErrorException: The message received from the server could not be parsed. Common causes for this error are when the response is modified by calls to Response.Write(), response filters, HttpModules, or server trace is enabled.
Details: Error parsing near '[The first bit of the CSV file]'.
I'm at a loss, and a deadline is fast approaching. Any help is greatly appreciated.
My guess is that you are in an AJAX Update Panel. If you are doing this type of action it must be done via a postback.
You can't do this with a partial/async postback with Ajax. You'll need to make whatever button you have triggering this download a PostBackTrigger for your UpdatePanel. Details here: http://www.asp.net/Ajax/Documentation/Live/mref/T_System_Web_UI_PostBackTrigger.aspx

Categories

Resources