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.
Related
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);
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.
hi there i am working on an asp.net web page that first lets users to enter required values to indicated textfields and then generate a new text file according to the data given by the user.
I want to let users receive a "Result.xml" file when they click on "get file" button.
i've searched for that info, i know it must have an easy solution but by now i am out of luck (i must be tired). i tried the following code but it didnt worked:
DataSet ds = new DataSet();
ds.Tables.Add("TEST");
ds.writexml("test.xml");
Response.TransmitFile("test.xml");
but the program says it couldnt find any file. also i dont want to "write" a physical file to the server, i just want to send the contents of the dataset as "test.xml"
Please help,
Thanks for spending your time.
Well assuming you are able to populate your DataSet properly, you should be able to use DataSet.WriteXML as in your example to write to a MemoryStream, then dump the file to the HTTP response without ever hitting your server's disk.
I'm a little rusty with C#, this example is from VB, so please take syntax with a grain of salt:
System.IO.MemoryStream objStream = new System.IO.MemoryStream();
System.Data.DataSet ds = new System.Data.DataSet();
ds.Tables.Add("TEST");
ds.WriteXml(objStream);
Response.Clear();
Response.Buffer = true;
Response.ContentType = "text/xml";
Response.AddHeader("Content-Disposition", "attachment; filename=File.xml");
Response.BinaryWrite(objStream.ToArray());
Response.End();
You can use the below code to write a XML file to the Client machine. Here we
write the contents directly into the Response.OutputStream using the XmlTextWriter:
try
{
Response.Clear();
Response.ContentType = "text/xml";
Response.AppendHeader("Content-Disposition","attachment;filename=FileName.xml");
XmlTextWriter xWriter = new XmlTextWriter(Response.OutputStream, System.Text.Encoding.UTF8);
xWriter.Formatting = Formatting.Indented;
xWriter.WriteStartDocument();
//Create Parent element
xWriter.WriteStartElement("Parent");
//Create Child elements
xWriter.WriteStartElement("Element");
xWriter.WriteElementString("ID", "1001");
xWriter.WriteElementString("Name", "John");
xWriter.WriteElementString("Age", "22");
xWriter.WriteEndElement();
//End writing top element and XML document
xWriter.WriteEndElement();
xWriter.WriteEndDocument();
xWriter.Close();
Response.End();
}
Whenever you work with files in Asp.Net you should use Server.MapPath. If you use relative paths your code will probably try to write to the IIS folder and might (hopefully) not be allowed to do so. So instead of using "test.xml" you should use
Server.MapPath("~/text.xml")
The tilde expands to your web application folder. That said it's a bad idea to store the file to disc. The Response object has an OutputStream which can be used to send data to the browser. You should write your data set directly into that stream. So no file has to be written to disc, you don't have to care about paths and your code will also work with multiple concurrent requests.
You can have the link link to an IHTTPHandler with the folowing code:
public class YourHandler : IHttpHandler
{
public void ProcessRequest(HttpContext context)
{
Response.Clear();
Response.ContentType = "text/xml";
Response.AppendHeader("Content-Disposition", String.Format("attachment;filename={0}", XML_FileName));
Response.TransmitFile(Server.MapPath("MyFile.xml"));
Response.End();
}
public bool IsReusable
{
get
{
return false;
}
}
}
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);
}
I need to construct and send a zip to a user.
I've seen examples doing one or the other, but not both, and am curious if there are any 'best practices' or anything.
Sorry for the confusion. I'm going to generating the zip on the fly for the web user, and sending it to them in the HTTP response. Not in an email.
Mark
I would second the vote for SharpZipLib to create the Zip file. Then you'll want to append a response header to the output to force the download dialog.
http://aspalliance.com/259
should give you a good starting point to achieve that. You basically need to add a response header, set the content type and write the file to the output stream:
Response.AppendHeader( "content-disposition", "attachment; filename=" + name );
Response.ContentType = "application/zip";
Response.WriteFile(pathToFile);
That last line could be changed to a Response.Write(filecontents) if you don't want to save to a temp file.
DotNetZip lets you do this easily, without ever writing to a disk file on the server. You can write a zip archive directly out to the Response stream, which will cause the download dialog to pop on the browser.
Example ASP.NET code for DotNetZip
More example ASP.NET code for DotNetZip
snip:
Response.Clear();
Response.BufferOutput = false; // false = stream immediately
System.Web.HttpContext c= System.Web.HttpContext.Current;
String ReadmeText= String.Format("README.TXT\n\nHello!\n\n" +
"This is text for a readme.");
string archiveName= String.Format("archive-{0}.zip",
DateTime.Now.ToString("yyyy-MMM-dd-HHmmss"));
Response.ContentType = "application/zip";
Response.AddHeader("content-disposition", "filename=" + archiveName);
using (ZipFile zip = new ZipFile())
{
zip.AddFiles(f, "files");
zip.AddFileFromString("Readme.txt", "", ReadmeText);
zip.Save(Response.OutputStream);
}
Response.Close();
or in VB.NET:
Response.Clear
Response.BufferOutput= false
Dim ReadmeText As String= "README.TXT\n\nHello!\n\n" & _
"This is a zip file that was generated in ASP.NET"
Dim archiveName as String= String.Format("archive-{0}.zip", _
DateTime.Now.ToString("yyyy-MMM-dd-HHmmss"))
Response.ContentType = "application/zip"
Response.AddHeader("content-disposition", "filename=" + archiveName)
Using zip as new ZipFile()
zip.AddEntry("Readme.txt", "", ReadmeText, Encoding.Default)
'' filesToInclude is a string[] or List<String>
zip.AddFiles(filesToInclude, "files")
zip.Save(Response.OutputStream)
End Using
Response.Close
I'm sure others will recommend SharpZipLib
How do you intend to "send" it. .NET has built in Libraries for email via SMTP
EDIT
In that case you'll want to capture the output stream from SharpZipLib and write it directly to the Response. Just make sure you have the correct Mimetype set in the Response Headers (application/zip) and make sure you don't Response.Write anything else to the user.
Agree with above, SharpZipLib , for creating .zip files in .NET, it seems a very popular option.
As for 'send'ing, if you mean via SMTP/Email, you will need to use the System.Net.Mail name space. The System.Net.Mail.Attachment Class documentation has an example of how to send a file via email
Scratch the above, by the time I posted this I see you meant return via HTTP Response.
One concern is the size of the file that you will be streaming to the client. If you use SharpZipLib to build the ZIP in-memory, you don't have any temp files to cleanup, but you'll soon run into memory issues if the files are large and a number of concurrent users are downloading files. (We experienced this pretty frequently when ZIP sizes got to the 200 MB+ range.) I worked around this by the temp file to disk, streaming it to the user, and deleting it when then request completed.
DotNetZip creates the stream without saving any resources on the server, so you don't have to remember to erase anything. As I said before, its fast and intuitive coding, with an efficient implementation.
Moshe
I modified some codes as below, i use System.IO.Compression
public static void Zip(HttpResponse Response, HttpServerUtility Server, string[] pathes)
{
Response.Clear();
Response.BufferOutput = false;
string archiveName = String.Format("archive-{0}.zip",
DateTime.Now.ToString("yyyy-MMM-dd-HHmmss"));
Response.ContentType = "application/zip";
Response.AddHeader("content-disposition", "filename=" + archiveName);
var path = Server.MapPath(#"../TempFile/TempFile" + DateTime.Now.Ticks);
if (!Directory.Exists(Server.MapPath(#"../TempFile")))
Directory.CreateDirectory(Server.MapPath(#"../TempFile"));
if (!Directory.Exists(path))
Directory.CreateDirectory(path);
var pathzipfile = Server.MapPath(#"../TempFile/zip_" + DateTime.Now.Ticks + ".zip");
for (int i = 0; i < pathes.Length; i++)
{
string dst = Path.Combine(path, Path.GetFileName(pathes[i]));
File.Copy(pathes[i], dst);
}
if (File.Exists(pathzipfile))
File.Delete(pathzipfile);
ZipFile.CreateFromDirectory(path, pathzipfile);
{
byte[] bytes = File.ReadAllBytes(pathzipfile);
Response.OutputStream.Write(bytes, 0, bytes.Length);
}
Response.Close();
File.Delete(pathzipfile);
Directory.Delete(path, true);
}
this code gets some list containing list of files copy them in temp folder
, create temp zip file then read all bytes of that file and send bytes in response stream, finaly delete temp file and folder