I've got the problem as below:
There is some SOAP web service which allows to read stream files. I need to read the whole file divided to chunks and transmit to user. All actions should do not block UI main thread: user presses 'Save' button on save file dialog, and is able to move on to the next page or perform another action. I will be grateful for the sample solution. Note that the solution should work with IIS 5.1.
Regards,
Jimmy
Downloading a file in ASP.NET byte-by-byte to the response page. check at msdn about this:
try
{
System.String filename = "C:\\downloadJSP\\myFile.txt";
// set the http content type to "APPLICATION/OCTET-STREAM
Response.ContentType = "APPLICATION/OCTET-STREAM";
// initialize the http content-disposition header to
// indicate a file attachment with the default filename
// "myFile.txt"
System.String disHeader = "Attachment;
Filename=\"myFile.txt\"";
Response.AppendHeader("Content-Disposition", disHeader);
// transfer the file byte-by-byte to the response object
System.IO.FileInfo fileToDownload = new
System.IO.FileInfo(filename);
System.IO.FileStream fileInputStream = new
System.IO.FileStream(fileToDownload.FullName,
System.IO.FileMode.Open, System.IO.FileAccess.Read);
int i;
while ((i = fileInputStream.ReadByte()) != - 1)
{
Response.Write((char)i);
}
fileInputStream.Close();
Response.Flush();
Response.Close();
}
catch (System.Exception e)
// file IO errors
{
SupportClass.WriteStackTrace(e, Console.Error);
}
There are some articles that may help you to implement and solve errors:
download an excel file from byte() on https server
asp.net downloading file from ftp getting it as byte[] then saving it as file
Remote file Download via ASP.NET corrupted file
Response.WriteFile cannot download a large file
ProcessRequest method form downloader HttpHandler:
public void ProcessRequest(HttpContext context)
{
RequestTarget target = RequestTarget.ParseFromQueryString(context.Request.QueryString);
Guid requestId = new Guid(context.Request.QueryString["requestId"]);
string itemName = HttpUtility.UrlDecode(context.Request.QueryString["itemName"]);
if (target != null &&
!requestId.Equals(Guid.Empty) &&
!string.IsNullOrEmpty(itemName))
{
HttpResponse response = context.Response;
response.Buffer = false;
response.Clear();
response.AddHeader("Content-Disposition", "attachment;filename=\"" + itemName + "\"");
response.ContentType = "application/octet-stream";
int length = 100000, i = 0;
byte[] fileBytes;
do
{
fileBytes = WS.ReadFile(requestId, target, i * length, length);
i++;
response.OutputStream.Write(fileBytes, 0, fileBytes.Length);
response.Flush();
}
while (fileBytes != null && fileBytes.Length == length);
}
}
The whole problem is not to organize download action, but satisfy the condition that download action should do not block UI main thread: user presses 'Save' button on save file dialog, and is able to move on to the next page or perform another action. The solution written by Niranjan Kala causes, when the file is very large user isn't able to see another page until the download action has completed. I appreciate it, but it's not what I meant ...
If I understand you correctly, you want to make the browser initiate a new request for the file without reloading the current page. The easiest approach is probably to just create a link with target="_blank". Something like this should do:
Download file
If you provide a content type of application/octet-stream most browsers will save the file to disk.
Related
I have a popup window that displays "Please wait while your file is being downloaded". This popup also executes the code below to start the file download. How can I close the popup window once the file download has completed? I need some way to detect that the file download has completed so I can call self.close() to close this popup.
System.Web.HttpContext.Current.Response.ClearContent();
System.Web.HttpContext.Current.Response.Clear();
System.Web.HttpContext.Current.Response.ClearHeaders();
System.Web.HttpContext.Current.Response.ContentType = fileObject.ContentType;
System.Web.HttpContext.Current.Response.AppendHeader("Content-Disposition", string.Concat("attachment; filename=", fileObject.FileName));
System.Web.HttpContext.Current.Response.WriteFile(fileObject.FilePath);
Response.Flush();
Response.End();
An idea:
If you handle the file downloading yourself in server side code by writing chunk by chunk to the response stream, then you'll know when the file had finished downloading. You would simply have to connect the FileStream to the response stream, send data chunk by chunk, and redirecting after complete. This can be inside your popup window.
Response.ContentType = "application/octet-stream";
Response.AppendHeader("content-disposition", "attachment; filename=bob.mp3");
Response.AppendHeader("content-length", "123456789");
Make sure you check Response.IsClientConnected when writing out to the response stream.
There is a solution where you can track the download status by transferring the file as smaller packets and check whether all the packets have been transferred.
The solution is not mine but you can find it here:
File Download in ASP.NET and Tracking the Status of Success/Failure of Download
//Function for File Download in ASP.Net in C# and
//Tracking the status of success/failure of Download.
private bool DownloadableProduct_Tracking()
{
//File Path and File Name
string filePath = Server.MapPath("~/ApplicationData/DownloadableProducts");
string _DownloadableProductFileName = "DownloadableProduct_FileName.pdf";
System.IO.FileInfo FileName = new System.IO.FileInfo(filePath + "\\" + _DownloadableProductFileName);
FileStream myFile = new FileStream(filePath + "\\" + _DownloadableProductFileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
//Reads file as binary values
BinaryReader _BinaryReader = new BinaryReader(myFile);
//Ckeck whether user is eligible to download the file
if (IsEligibleUser())
{
//Check whether file exists in specified location
if (FileName.Exists)
{
try
{
long startBytes = 0;
string lastUpdateTiemStamp = File.GetLastWriteTimeUtc(filePath).ToString("r");
string _EncodedData = HttpUtility.UrlEncode(_DownloadableProductFileName, Encoding.UTF8) + lastUpdateTiemStamp;
Response.Clear();
Response.Buffer = false;
Response.AddHeader("Accept-Ranges", "bytes");
Response.AppendHeader("ETag", "\"" + _EncodedData + "\"");
Response.AppendHeader("Last-Modified", lastUpdateTiemStamp);
Response.ContentType = "application/octet-stream";
Response.AddHeader("Content-Disposition", "attachment;filename=" + FileName.Name);
Response.AddHeader("Content-Length", (FileName.Length - startBytes).ToString());
Response.AddHeader("Connection", "Keep-Alive");
Response.ContentEncoding = Encoding.UTF8;
//Send data
_BinaryReader.BaseStream.Seek(startBytes, SeekOrigin.Begin);
//Dividing the data in 1024 bytes package
int maxCount = (int)Math.Ceiling((FileName.Length - startBytes + 0.0) / 1024);
//Download in block of 1024 bytes
int i;
for (i = 0; i < maxCount && Response.IsClientConnected; i++)
{
Response.BinaryWrite(_BinaryReader.ReadBytes(1024));
Response.Flush();
}
//if blocks transfered not equals total number of blocks
if (i < maxCount)
return false;
return true;
}
catch
{
return false;
}
finally
{
Response.End();
_BinaryReader.Close();
myFile.Close();
}
}
else System.Web.UI.ScriptManager.RegisterStartupScript(this, GetType(),
"FileNotFoundWarning","alert('File is not available now!')", true);
}
else
{
System.Web.UI.ScriptManager.RegisterStartupScript(this, GetType(),
"NotEligibleWarning", "alert('Sorry! File is not available for you')", true);
}
return false;
}
Even though this is an old question it hasn't been answered all this time and I believe it deserves a (better) answer:
https://stackoverflow.com/a/59010319/313935
Some hacks are around that involves knowing when the last piece of the buffer has been sent or checking the HttpResponse.IsClientConnected property.
The way to do that is in your pop-up to call the server via AJAX polling for some response which would indicate the file was flushed.
Ex: right before sending the file, store sessionID+FileName in a DB or session or what have you.
On the client, in your popup, poll a web-service via AJAX - this could even be a WebMethod like Bool IsContentFlushed(string sessionID, string fileName);
After you do Response.Flush(); remove this sessionID+FileName from your store.
Call Response.Close() instead of Response.End() - the later is very brutal, and is usually over-kill.
I handle the problem differently in Javascript, which might or might not work for you.
Create a hidden DIV element, with the
message 'File is downloading...'
rather than a pop-up box.
Show the div when the download
starts
Once any other element on the
forms is clicked, hide the div
again..
You could also set a timer to hide
the download message div after so
amount of time...
I figure once the user clicks on another element, she either already knows the download is done, or she is ready to do something else, so the message becomes irrelevant and can go away....
I have a file that is located at a network drive. The user access is already created to have full access to the path. But it seems that when I ran the following code to get the file, the browser just does not respond.
FileInfo file = new FileInfo(GetDocumentUploadFolder(ID) + fileName);
// Checking if file exists
if (file.Exists)
{
// Clear the content of the response
this.Page.Response.ClearContent();
// Clear the header of the response
this.Page.Response.ClearHeaders();
// Set the ContentType
this.Page.Response.ContentType = "application/pdf";
// Write the file into the response (TransmitFile is for ASP.NET 2.0. In ASP.NET 1.1 you have to use WriteFile instead)
this.Page.Response.WriteFile(file.FullName);
// End the response
this.Page.Response.End();
}
I tried using this.Page.Response.TransmitFile(file.FullName); and it also does not work. The page seems to stop functioning after this.Page.Response.End();
Any ideas?
No matter where file a stored. Your action must return a file as the result:
public FileResult GetBytes()
{
string path = Server.MapPath("~/Files/PDFIcon.pdf");
byte[] mas = System.IO.File.ReadAllBytes(path);
string file_type = "application/pdf";
string file_name = "PDFIcon.pdf";
return File(mas, file_type, file_name);
}
Server.MapPath(filePath string) - must have access to the file.
I am able to do a workaround by copying the files first from the network drive to local path and then do a TransmitFile from there:
FileInfo file = new FileInfo(GetDocumentUploadFolder(ID) + fileName);
string strFolder = Server.MapPath(LocalLocation);
string strDestination = Server.MapPath(LocalLocation + "\\" + fileName);
// Checking if file exists
if (file.Exists)
{
if (!Directory.Exists(strFolder))
Directory.CreateDirectory(strFolder);
// Delete contents in this folder
Common.DeleteFiles(strFolder, "*.*");
file.CopyTo(strDestination, true);
// Clear the content of the response
this.Page.Response.ClearContent();
// Clear the header of the response
this.Page.Response.ClearHeaders();
// Set the ContentType
this.Page.Response.ContentType = "application/pdf";
// Write the file into the response (TransmitFile is for ASP.NET 2.0. In ASP.NET 1.1 you have to use WriteFile instead)
this.Page.Response.TransmitFile(strDestination);
// End the response
this.Page.Response.End();
}
Here's the logic for downloading the file.
It automatically downloads the file, whether it's a .jpg, .pdf, .zip or whatever, I would like it to only open in a new tab if it's an image file(jpg, png, etc.) and then everything else prompts for a download.
I feel like I'm missing something when it comes to solving this. :/
public void DownloadArchivedFiles(ArchiveType type, Object id, String fileName)
{
Response.ContentType = "application/zip";
Response.AddHeader("content-disposition", "filename=" + fileName);
String path = Server.MapPath(String.Format("~/{0}Files/{1}", type, id));
var fmFiles = new string[0];
var files=new string[0];
if(type==ArchiveType.Issue)
{
fmFiles = _files.GetIssueFiles(new Guid(id.ToString())).Select(x => Server.MapPath("~" + x.FilePath)).ToArray();
}else if(type==ArchiveType.Task)
{
fmFiles = _files.GetTaskFiles(int.Parse(id.ToString())).Select(x => Server.MapPath("~" + x.FilePath)).ToArray();
}
if (!System.IO.Directory.Exists(path) && !fmFiles.Any()) return;
//String[] files = System.IO.Directory.GetFiles(Server.MapPath(String.Format("~/{0}Files/{1}", type, id)));
try
{
files = System.IO.Directory.GetFiles(Server.MapPath(String.Format("~/{0}Files/{1}", type, id)));
}
catch (Exception)
{
;
}
using (ZipFile zip = new ZipFile())
{
zip.AddFiles(files, "/");
if(fmFiles.Any())
zip.AddFiles(fmFiles,"/");
zip.Save(Response.OutputStream);
}
HttpContext.Response.End();
}
You can't open a new tab by providing a header in the response. It's too late. By the time the response is being generated, the browser is already trying to get the response stream into the same tab, and the old document may already have been unloaded.
Instead, you need to change the way the request is sent. Specifically, the link for the image must have the _target="blank" as an attribute. This tells the browser to open up a new tab (or window, depending on the user's browser settings) and then request the image there. You don't need to do anything special to the response headers at that point.
You should avoid target="blank" for your downloadable files (e.g. PDFs). It'll work ok on some browsers, but on others you'll end up with unsightly zombie tabs.
I have been following these links all listed below, i found the best way to write this SMALL create Excel and Download function. ( Using EPPlus for Excel )
Download file of any type in Asp.Net MVC using FileResult? + How to convert an Stream into a byte[] in C#?
Using a FileStreamResult with a MemoryStream in ASP.NET MVC 3
Writing A Custom File Download Action Result For ASP.NET MVC
It runs through the code perfectly without error every time I run this but does not "Kick out" the file to be downloaded ( in a save as dialogue or w/e ).
public ActionResult ShowReport()
{
using (var stream = new MemoryStream())
{
ExcelPackage pck = new ExcelPackage();
var ws = pck.Workbook.Worksheets.Add("Sample1");
ws.Cells["A1"].Value = "Sample 1";
ws.Cells["A1"].Style.Font.Bold = true;
var shape = ws.Drawings.AddShape("Shape1", eShapeStyle.Rect);
shape.SetPosition(50, 200);
shape.SetSize(200, 100);
shape.Text = "Sample 1 text text text";
var fileDownloadName = "sample.xlsx";
var contentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";//System.Net.Mime.MediaTypeNames.Application.Octet
var fileStream = new MemoryStream();
pck.SaveAs(fileStream);
fileStream.Position = 0;
var fsr = new FileStreamResult(fileStream, contentType);
fsr.FileDownloadName = fileDownloadName;
byte[] fileBytes = ReadToEnd(fileStream);
string fileName = "example";
return File(fileBytes, contentType, fileName);
}
}
What am I doing wrong / missing? - Must i write that Dialogue myself?
PN: I have also attempted this way
byte[] fileBytes = ReadToEnd(fileStream);
string fileName = "example";
return File(fileBytes, contentType, fileName);
ofcourse i had to figure out how to convert Stream to Byte but it also did not show anything.
Image of Chrome's Network Development Tool
Sorry about the small image ( if you can't see it scroll in with ctl+MouseWheel ) if your in a supporting browswer.
(In response to the comment thread above.)
From the image posted it looks like the actual file request (the last one in the list) is coming from JavaScript code instead of from a normal document-level request. Given this, it's highly likely that the server-side code is working correctly and returning the correct response.
However, since it's an AJAX request, the browser doesn't actually know what to do with the response. There are some potential solutions here. Ideally, you'll want to make this a normal request and remove AJAX from the picture if possible. If that's not an option, you can still initiate a document-level request from JavaScript. Something as simple as this:
window.location = '#Url.Action("Method", "Controller")';
This would be initiated from JavaScript code as it currently is, but would be for the whole browser instead of an AJAX request. That should do the trick.
Using the memory stream you have you can simple pass that to the Response object once you have saved the Excel Package
Code:
Response.AddHeader("content-disposition", "attachment;filename=FILENAME.xlsx")
Response.Charset = String.Empty
Response.ContentType = "application/ms-excel"
Response.BinaryWrite(stream.ToArray())
Response.End()
I have a popup window that displays "Please wait while your file is being downloaded". This popup also executes the code below to start the file download. How can I close the popup window once the file download has completed? I need some way to detect that the file download has completed so I can call self.close() to close this popup.
System.Web.HttpContext.Current.Response.ClearContent();
System.Web.HttpContext.Current.Response.Clear();
System.Web.HttpContext.Current.Response.ClearHeaders();
System.Web.HttpContext.Current.Response.ContentType = fileObject.ContentType;
System.Web.HttpContext.Current.Response.AppendHeader("Content-Disposition", string.Concat("attachment; filename=", fileObject.FileName));
System.Web.HttpContext.Current.Response.WriteFile(fileObject.FilePath);
Response.Flush();
Response.End();
An idea:
If you handle the file downloading yourself in server side code by writing chunk by chunk to the response stream, then you'll know when the file had finished downloading. You would simply have to connect the FileStream to the response stream, send data chunk by chunk, and redirecting after complete. This can be inside your popup window.
Response.ContentType = "application/octet-stream";
Response.AppendHeader("content-disposition", "attachment; filename=bob.mp3");
Response.AppendHeader("content-length", "123456789");
Make sure you check Response.IsClientConnected when writing out to the response stream.
There is a solution where you can track the download status by transferring the file as smaller packets and check whether all the packets have been transferred.
The solution is not mine but you can find it here:
File Download in ASP.NET and Tracking the Status of Success/Failure of Download
//Function for File Download in ASP.Net in C# and
//Tracking the status of success/failure of Download.
private bool DownloadableProduct_Tracking()
{
//File Path and File Name
string filePath = Server.MapPath("~/ApplicationData/DownloadableProducts");
string _DownloadableProductFileName = "DownloadableProduct_FileName.pdf";
System.IO.FileInfo FileName = new System.IO.FileInfo(filePath + "\\" + _DownloadableProductFileName);
FileStream myFile = new FileStream(filePath + "\\" + _DownloadableProductFileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
//Reads file as binary values
BinaryReader _BinaryReader = new BinaryReader(myFile);
//Ckeck whether user is eligible to download the file
if (IsEligibleUser())
{
//Check whether file exists in specified location
if (FileName.Exists)
{
try
{
long startBytes = 0;
string lastUpdateTiemStamp = File.GetLastWriteTimeUtc(filePath).ToString("r");
string _EncodedData = HttpUtility.UrlEncode(_DownloadableProductFileName, Encoding.UTF8) + lastUpdateTiemStamp;
Response.Clear();
Response.Buffer = false;
Response.AddHeader("Accept-Ranges", "bytes");
Response.AppendHeader("ETag", "\"" + _EncodedData + "\"");
Response.AppendHeader("Last-Modified", lastUpdateTiemStamp);
Response.ContentType = "application/octet-stream";
Response.AddHeader("Content-Disposition", "attachment;filename=" + FileName.Name);
Response.AddHeader("Content-Length", (FileName.Length - startBytes).ToString());
Response.AddHeader("Connection", "Keep-Alive");
Response.ContentEncoding = Encoding.UTF8;
//Send data
_BinaryReader.BaseStream.Seek(startBytes, SeekOrigin.Begin);
//Dividing the data in 1024 bytes package
int maxCount = (int)Math.Ceiling((FileName.Length - startBytes + 0.0) / 1024);
//Download in block of 1024 bytes
int i;
for (i = 0; i < maxCount && Response.IsClientConnected; i++)
{
Response.BinaryWrite(_BinaryReader.ReadBytes(1024));
Response.Flush();
}
//if blocks transfered not equals total number of blocks
if (i < maxCount)
return false;
return true;
}
catch
{
return false;
}
finally
{
Response.End();
_BinaryReader.Close();
myFile.Close();
}
}
else System.Web.UI.ScriptManager.RegisterStartupScript(this, GetType(),
"FileNotFoundWarning","alert('File is not available now!')", true);
}
else
{
System.Web.UI.ScriptManager.RegisterStartupScript(this, GetType(),
"NotEligibleWarning", "alert('Sorry! File is not available for you')", true);
}
return false;
}
Even though this is an old question it hasn't been answered all this time and I believe it deserves a (better) answer:
https://stackoverflow.com/a/59010319/313935
Some hacks are around that involves knowing when the last piece of the buffer has been sent or checking the HttpResponse.IsClientConnected property.
The way to do that is in your pop-up to call the server via AJAX polling for some response which would indicate the file was flushed.
Ex: right before sending the file, store sessionID+FileName in a DB or session or what have you.
On the client, in your popup, poll a web-service via AJAX - this could even be a WebMethod like Bool IsContentFlushed(string sessionID, string fileName);
After you do Response.Flush(); remove this sessionID+FileName from your store.
Call Response.Close() instead of Response.End() - the later is very brutal, and is usually over-kill.
I handle the problem differently in Javascript, which might or might not work for you.
Create a hidden DIV element, with the
message 'File is downloading...'
rather than a pop-up box.
Show the div when the download
starts
Once any other element on the
forms is clicked, hide the div
again..
You could also set a timer to hide
the download message div after so
amount of time...
I figure once the user clicks on another element, she either already knows the download is done, or she is ready to do something else, so the message becomes irrelevant and can go away....