ServletOutputStream closes prematurely - servlets

I wrote a servlet to download a file. it works well for some files, but for others the os closes prematurely, and I end up downloading a blank file.
File file = new File(fileName);
InputStream fis = new FileInputStream(file);
resp.setHeader("Content-Type", "text/plain");
resp.setHeader("Content-Disposition", "attachment; filename=\"" + fileName );
resp.setDateHeader("Expires",0);
resp.setHeader("Cache-Control", "must-revalidate, post-check=0, pre-check=0");
resp.setHeader("Pragma", "public");
resp.setContentLength((int) file.length());
resp.setHeader("Content-Disposition", "attachment; filename=\"" + fileName + "\"");
ServletOutputStream os = resp.getOutputStream();
byte[] bufferData = new byte[1024];
int read = 0;
while((read = fis.read(bufferData))!= -1){
System.out.println("read " + read + " chars");
os.write(bufferData, 0, read);
}
os.flush();
os.close();
fis.close();
when I debug, I can see it trying to write the first buffer worth of data into os, then on the browser side, the download dialog pops up with a blank file. in the servlet, I get a IOClosed on the next buffer write.
I tried using IOUtils.copy(fis, resp.getOutputStream());
get the same error.
any reason certain type of file data can force os to close prematurely? can I protect against it?

Related

Getting Out of Memory Exception when downloading large files

I developed ASP.NET web application. I am trying to download the document from file server. FileSize maybe more than 1GB.
Here the thing is, very first time file got downloaded. when i click the same link, i am getting "Out of memory Exception" throws error.
If it is small size file, i don't get any issues to download the filles...
How do we handle this error if i download the document more than 1GB...?
below is the code to download the file in ASP.NET (Code behind file)
Dim binFile As Byte() = File.ReadAllBytes(filename)
Response.ContentType = "application/octet-stream"
Response.AppendHeader("Content-Disposition", "attachment; filename=" + filename)
Response.BinaryWrite(binFile)
Response.End()
Response.BinaryWrite() buffers whole file into memory, you can Use HttpResponse.TransmitFile() which don't buffer the file in memory.
Better solution is to send file as chunks, stream and ...
Here is a sample for download file without buffer
using (FileStream fileStream = File.OpenRead("full path to file"))
{
int bufferSize = 10 * 1024;
byte[] buffer = new byte[bufferSize];
long dataToRead = fileStream.Length;
Response.Clear();
Response.ContentType = "application/octet-stream";
Response.AddHeader("Content-Disposition", "attachment; filename=FILE_NAME_THAT_USER_MUST_SEE");
Response.AddHeader("Content-Length", fileStream.Length.ToString());
while (dataToRead > 0)
if (Response.IsClientConnected)
{
int readLength = fileStream.Read(buffer, 0, bufferSize);
Response.OutputStream.Write(buffer, 0, readLength);
Response.Flush();
buffer = new byte[bufferSize - 1 + 1];
dataToRead -= readLength;
}
else
break;
}
Response.Close();

spring mvc download file not showing extension

I'm trying to let customers download sample excel file. Here is my code:
String path = UploadController.class.getResource("/com/medify/data/" + file).getFile();
response.setContentType("application/force-download");
response.setContentType("application/excel");
response.setHeader("Content-Disposition", "attachment; filename=" + file);
try{
File fi = new File(path);
InputStream in = new BufferedInputStream(new FileInputStream(fi));
ServletOutputStream out = response.getOutputStream();
IOUtils.copy(in, out);
response.flushBuffer();
return null;
}
catch(Exception ex){
return null;
}
The problem is the downloaded file never have file extension. The file is sample-doctor-uploads.xls and it exists in com/medify/data package.
You need to use proper content type. If your file has .xls extention you need to use -
response.contentType = "application/vnd.ms-excel"
and use the full file name with extention like-
response.setHeader "Content-disposition", "attachment;filename=fileNamewithExtention"

Server.map path not working in asp.net

I am using this code to download a excel file which exist in my solution. I have added a folder FileUpload and added a excel file UploadCWF.xlsx. My code is workin in local host. But not working when I host this to server.I am getting error - Could not find a part of the path. My code -
string filePath = HttpContext.Current.Server.MapPath("~/FileUpload/");
string _DownloadableProductFileName = "UploadCWF.xlsx";
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);
//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();
}
}
catch (Exception es)
{
throw es;
}
finally
{
Response.End();
_BinaryReader.Close();
myFile.Close();
}
}
else
System.Web.UI.ScriptManager.RegisterStartupScript(this, GetType(),
"FileNotFoundWarning", "alert('File is not available now!')", true);
Please some one help me.
You should first concat filepath and filename then get path using server.mappath.
You should write code like this
string filePath = HttpContext.Current.Server.MapPath("~/FileUpload/UploadCWF.xlsx");
System.IO.FileInfo FileName = new System.IO.FileInfo(filePath);

ASP.NET - response.outputstream.write either writes 16k and then all 0's, or writes all but insetrs a char every 64k

I have the following code...
public partial class DownloadFile : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
string FilePath = "[FTPPath]";
Download downloadFile = new Download();
Server.ScriptTimeout = 54000;
try
{
long size = downloadFile.GetFileSize(FilePath);
using (FtpWebResponse ftpResponse = downloadFile.BrowserDownload(FilePath))
using (Stream streamResponse = ftpResponse.GetResponseStream())
{
string fileName = FilePath.Substring(FilePath.LastIndexOf("/") + 1);
int bufferSize = 65536;
byte[] buffer = new byte[bufferSize];
int readCount;
readCount = streamResponse.Read(buffer, 0, bufferSize);
// Read file into buffer
//streamResponse.Read(buffer, 0, (int)size);
Response.Clear();
Response.Buffer = false;
Response.BufferOutput = false;
//Apparently this line helps with old version of IE that like to cache stuff no matter how much you tell them!
Response.AddHeader("Pragma", "public");
//Expires: 0 forces the browser to always thing the page is "stale" therefore forcing it to never cache the page and therefore always re-downloads the page when viewed. Therefore no nasty experiences if we change the authentication details.
Response.Expires = 0;
//Again this line forces the browser not to cache the page.
Response.AddHeader("Cache-Control", "no-cache, must-revalidate");
Response.AddHeader("Cache-Control", "public");
Response.AddHeader("Content-Description", "File Transfer");
Response.ContentType = "application/zip";
Response.AddHeader("Content-Disposition", "attachment; filename=" + fileName);
Response.AddHeader("Content-Transfer-Encoding", "binary");
Response.AddHeader("Content-Length", size.ToString());
// writes buffer to OutputStream
while (readCount > 0)
{
Response.OutputStream.Write(buffer, 0, bufferSize);
readCount = streamResponse.Read(buffer, 0, bufferSize);
Response.Flush();
}
Response.End();
Server.ScriptTimeout = 90;
}
}
catch (Exception ex)
{
Response.Write("<p>" + ex.Message + "</p>");
Server.ScriptTimeout = 90;
}
}
}
To download .zip files from an FTP (please ignore the header rubbish about preventing caching unless this is related to the issue).
So downloadFile is a class I have written using FTPWebRequest/Response with SSL enabled that can do to two things; one is return the file size (GetFileSize) of a file on our FTP and the other is to set FtpWebRequest.Method = WebRequestMethods.Ftp.DownloadFile to allow the download of a file.
Now the code appears to work perfectly, you get a nice zip downloaded of exactly the same size as the one on the FTP however, this is where the quirks begin.
The zip files are always corrupted, no matter how small. In theory, very small files should be okay, but you'll see why in a moment. Because of this, I decided to compare the files in binary.
If I set bufferSize to anything other than the size of the file
(i.e. 1024, 2048, 65536), the first 16k (16384 bytes) downloads
perfectly, and then the stream just writes zeros to the end of the
file.
If I set bufferSize = size (filesize), the stream appears to download the full file, until you look more closely. The file is an exact replica up to the first 64k, and then an extra character appears in the downloaded file (this chararacter never seems to be the same).
After this extra byte, the files are exactly the same again. An extra byte appears to get added every 64k, meaning that by the end of 65MB file, the two files are massively out of sync. Because the download length is limited to the size of the file on the server, the end of the file gets truncated in the downloaded file. The archive will allow access to it as all the CRC checks fail.
Any help would be much appreciated. Cheers.
Now changed my code somewhat to use WebRequest and WebResponse to grabe a zip using Http from the web server itself. Here is the code...
public partial class DownloadFile : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
string FilePath = [http path];
Server.ScriptTimeout = 54000;
try
{
WebRequest HWR = WebRequest.Create(FilePath);
HWR.Method = WebRequestMethods.File.DownloadFile;
using (WebResponse FWR = HWR.GetResponse())
using (BinaryReader streamResponse = new BinaryReader(FWR.GetResponseStream()))
{
string fileName = FilePath.Substring(FilePath.LastIndexOf("/") + 1);
int bufferSize = 2048;
byte[] buffer = new byte[bufferSize];
int readCount;
readCount = streamResponse.Read(buffer, 0, bufferSize);
Response.Clear();
Response.Buffer = false;
Response.BufferOutput = false;
//Apparently this line helps with old version of IE that like to cache stuff no matter how much you tell them!
Response.AddHeader("Pragma", "public");
//Expires: 0 forces the browser to always thing the page is "stale" therefore forcing it to never cache the page and therefore always re-downloads the page when viewed. Therefore no nasty experiences if we change the authentication details.
Response.Expires = 0;
//Again this line forces the browser not to cache the page.
Response.AddHeader("Cache-Control", "no-cache, must-revalidate");
Response.AddHeader("Cache-Control", "public");
Response.AddHeader("Content-Description", "File Transfer");
Response.ContentType = "application/zip";
Response.AddHeader("Content-Disposition", "attachment; filename=" + fileName);
Response.AddHeader("Content-Transfer-Encoding", "binary");
// writes buffer to OutputStream
while (readCount > 0)
{
Response.OutputStream.Write(buffer, 0, bufferSize);
Response.Flush();
readCount = streamResponse.Read(buffer, 0, bufferSize);
}
//Response.Write(testString);
Response.End();
Server.ScriptTimeout = 90;
}
}
catch (Exception ex)
{
Response.Write("<p>" + ex.Message + "</p>");
Server.ScriptTimeout = 90;
}
}
}
This code is more simple but it is still corrupting the data. I'm sure there's something very simple I'm doing wrong, but I just can't spot it or find a test to show me where I am going wrong. Please help :)
On your line
Response.OutputStream.Write(buffer, 0, bufferSize);
change bufferSize to readCount so that you only write the number that you actually read.

Error: while trying to OPen PDF in ASP.NET

In my ASP.NET application,When I try to open PDF file by using the below code, I am getting an error
CODE USED TO SHOW PDF FILE
FileStream MyFileStream = new FileStream(filePath, FileMode.Open);
long FileSize = MyFileStream.Length;
byte[] Buffer = new byte[(int)FileSize + 1];
MyFileStream.Read(Buffer, 0, (int)MyFileStream.Length);
MyFileStream.Close();
Response.ContentType = "application/pdf";
Response.AddHeader("content-disposition", "attachment; filename="+filePath);
Response.BinaryWrite(Buffer);
ERROR I AMN GETTING
"There was an error opening this document.The file is damaged and could not open"
Sounds like your using an aspx file to output the pdf. Have you considered using an ashx file which is an HttpHandler? It bypasses all the typical aspx overhead stuff and is more efficient for just serving up raw data.
Here is an example of the ashx using your code:
<% WebHandler Language="c#" class="ViewPDF" %>
public class ViewPDF : IHttpHandler
{
public void ProcessRequest(HttpContext context)
{
FileStream MyFileStream = new FileStream(filePath, FileMode.Open);
long FileSize = MyFileStream.Length;
byte[] Buffer = new byte[(int)FileSize + 1];
MyFileStream.Read(Buffer, 0, (int)MyFileStream.Length);
MyFileStream.Close();
Response.ContentType = "application/pdf";
Response.AddHeader("content-disposition", "attachment; filename="+filePath);
Response.BinaryWrite(Buffer);
}
public bool IsReusable
{
get { return false; }
}
}
If you still want to use the aspx page. Make sure you are doing the following:
// At the beginning before you do any response stuff do:
Response.Clear();
// When you are done all your response stuff do:
Response.End();
That should solve your problem.
You must flush the response otherwise it gets partially transmitted.
Response.Flush();
In addition to ocedcio's reply, you need to be aware that Stream.Read() does not necessarily read all of the bytes requested. You should examine the return value from Stream.Read() and continue reading if less bytes are read than requested.
See this question & answer for the details: Creating a byte array from a stream

Resources