httpServletResponse behavior in chrome and firefox - java

I have a controller method in charge of an excel download:
#RequestMapping(value = "/report/user", method = RequestMethod.GET)
public void getUserReport(#RequestParam(value = "name", required = false) String user,
#RequestParam(value = "startdate", required = false) String startDate,
#RequestParam(value = "enddate", required = false) String endDate,
HttpServletResponse response) {
List<Survey> userSurveys = surveyService.findUserSurveys(user, startDate, endDate);
String userFileName = nameService.getUserFileName(user, startDate, endDate);
Workbook userExcelReport = excelReportGenerator.createUserExcelReport(userSurveys);
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
response.setHeader("Content-Disposition", "attachment; filename=" + userFileName + ".xlsx");
try {
userExcelReport.write(response.getOutputStream());
} catch (IOException e) {
logger.error("The excel workbook could not write to the outputStream ", e);
}
}
as you can see this controller mapping is tanking a excel workbook (done in apache poi) and setting the header and content type, in google chrome this works perfectly but in firefox the file is downloaded as a "file" with no extension (but the data is correct as if i open the file with excel all is there) and the name is built wrong, as in chrome this works completely fine i assume there is something wrong the way the response works in firefox, anyone has idea what could i be doing wrong?
example of downloaded files trough firefox
example of downloads using chrome

According RFC 6266 the filename value is either a token or a quoted-string.
A token must not contain separators. But white space is a separator. So you must using a quoted-string.
So try
response.setHeader("Content-Disposition", "attachment; filename=\"" + userFileName + ".xlsx\"");

Please check this way to sent content type in response. It works for me.
resp.setContentType( "application/octet-stream" );

Related

.zip file downloaded as f.txt file - springboot

Below code is always downloading a f.txt file rather downloading without the actual file name and extension(here .zip extension).
#RequestMapping(value = "/files/{fileId}", method = RequestMethod.GET, produces = "application/zip")
public ResponseEntity<Resource> downloadFile(#PathVariable("fileId") String fileName) {
log.info("Downloading file..!!!!");
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.valueOf("application/zip"));
log.info("Content info : "+headers.getContentType().toString());
File file = FileUtils.getFile("backup/" + fileName + ".zip");
log.info("File name is : "+file.getName());
FileSystemResource fileSystemResource = new FileSystemResource(file);
return new ResponseEntity<>(fileSystemResource, headers, HttpStatus.OK);
}
It would be great if someone can let me know where the mistake is/ some amendments to be done?
f.txt is coming from the Content-Disposition response header. This is a consequence of fixing cve-2015-5211 (RFD Attack)
To fix the issue, add the content-disposition and content-length headers:
...
log.info("File name is : "+file.getName());
// Adding the following two lines should fix the download for you:
headers.set("content-disposition", "inline; filename=\"" + file.getName() + "\"");
headers.set("content-length", String.valueOf(file.length()));
FileSystemResource fileSystemResource = new FileSystemResource(file);
...

Spring Controller download file from file system restriction

I have a Spring controller with /file mapping that gets a file name from user and stream file content to user
#RequestMapping(value = "/file" , method = RequestMethod.GET)
#ResponseBody
public void getFile(#RequestParam(value = "name", required = true) String fileName,
HttpServletResponse response)
{
String fileExtension = "";
int i = fileName.lastIndexOf('.');
if (i > 0) {
fileExtension = fileName.substring(i+1);
}
// file extension for requested file must be xls
if(!fileExtension.equals("xls"))
{
response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
return;
}
try {
Path path = Paths.get("/tmp/" + fileName);
byte[] data = Files.readAllBytes(path);
response.setHeader("Content-Disposition", "inline; filename=" + fileName);
response.setContentType("application/vnd.ms-excel");
response.setContentLength(data.length);
try {
ServletOutputStream outputStream = response.getOutputStream();
outputStream.write(data);
outputStream.flush();
} catch (Exception e) {
}
} catch (Exception e) {
}
}
User only can download file with .xls extension in tmp folder. the problem with this code is that user can change directory and download other .xls files in other directories. for example if there is a file in this path /tmp/tmp2/ab.xls user can download the file with calling this url http://myserver.mydomain:myport/mycontext/file?name=tmp2/ab.xls that is a security hole. what is the best way for checking name that I give from user is a file name? (not directory/filename or ../filename or another dangerous path )
Path tmpPath = Paths.get("/tmp/"); //valid directory
String fileName = "foo/bar.xls"; //supplied fileName
Path filePath = tmpPath.resolve(fileName); //add fileName to path
Path fileParent = filePath.getParent(); //get parent directory
System.out.println(fileParent);
System.out.println(tmpPath.equals(fileParent)); //false because fileParent is '/tmp/foo'
'tmpPath' will be equals 'fileParent' if you supply a valid fileName like 'bar.xls'.
I think you can also simplify the extension checking: filePath.endsWith(".xls"); should be enough. And don't concatenate file paths ("/tmp/" + fileName). Paths.get("/tmp", fileName) will do that for you.

JSF download file returns index.jsf

Im trying to achieve the same download file through JSF as described in those posts:
JSF2 download file returns xhtml page source
How to provide a file download from a JSF backing bean?
http://bharatonjava.wordpress.com/2013/02/01/downloading-file-in-jsf-2/
http://prabinhada.blogspot.com/2012/06/how-to-download-file-using-jsf.html
My problem is that I actually can download the file, but its name is just wrong ( on whichever browser ).
It's a XML file I marshall in the download method but browser always receives a file index.jsf ( but mime type is set at text/xml and file size correct )
It runs on:
Mojarra 2.1.7
Richfaces 4.3.3.Final
jboss-7.1.1.Final
Please see below if you spot any problem
public void exportEDL() throws Exception {
String name = this.file.getName();
FacesContext facesContext = FacesContext.getCurrentInstance();
ExternalContext externalContext = facesContext.getExternalContext();
HttpServletResponse response = (HttpServletResponse) externalContext.getResponse();
BufferedOutputStream output = null;
StringWriter sw = null;
try {
...
sw = new StringWriter();
// edl is the jaxb i want to return as xml file
marshaller.marshal(this.edl, sw);
// Init servlet response.
response.reset();
response.setBufferSize(DEFAULT_BUFFER_SIZE);
response.setContentType("text/xml");
response.setContentLength( sw.getBuffer().length() );
response.setHeader("Content-Disposition", "attachment;filename\"" + name + "\"");
output = new BufferedOutputStream(response.getOutputStream(), DEFAULT_BUFFER_SIZE);
output.write(sw.toString().getBytes("UTF-8"));
// Finalize task.
sw.flush();
output.flush();
} catch ( Exception e ) {
FacesMessage msg = new FacesMessage( FacesMessage.SEVERITY_ERROR, null, e.getMessage() );
facesContext.addMessage("Error", msg);
return;
} finally {
// Gently close streams.
IOUtil.close(sw);
IOUtil.close(output);
clearUploadData();
}
// Inform JSF that it doesn't need to handle response.
facesContext.responseComplete();
// facesContext.renderResponse();
// facesContext.release();
}
I tried with something else than a XML marshalling without better results.
Have you guys manage to download a file with correct name + extension?
It's a commandButton in the view:
<h:commandButton action="#{fileUploadBean.exportEDL()}"
Thanks!
You seem to be missing a =, I think this would be right:
response.setHeader("Content-Disposition", "attachment;filename=\"" + name + "\"");
or just
response.setHeader("Content-Disposition", "attachment;filename=" + name);

How do I return a zip file to the browser via the response OutputStream?

In this situation, I have created a zip file containing search result files, and am trying to send it to the user. Here is the chunk of code I am currently trying to use.
File[] zippable = new File[files.size()];
File resultFile = ZipCreator.zip(files.toArray(zippable), results);
InputStream result = new FileInputStream(resultFile);
IOUtils.copy(result, response.getOutputStream());
However, this currently doesn't work quite right. Instead of returning the zip file that I have created, it returns an html file. If I manually change the file extension afterwards, I can see that the contents of the file are still the search results that I need. So the problem just lies in returning the proper extension to the response.
Does anyone have any advice for this situation?
You need to set the Content-Type response header to the value application/zip (or application/octet-stream, depending on the target browser). Additionally, you may want to send additional response headers indicating attachment status and filename.
You need to set the content type header to application/octet-stream prior to streaming the results. Depends on what implementation of response you are using on how you actually do this.
Here is some working code, just in case anyone needs it:
protected void doGet(HttpServletRequest request, HttpServletResponse response) {
// The zip file you want to download
File zipFile = new File(zipsResourcesPath + zipFileName);
response.setContentType("application/zip");
response.addHeader("Content-Disposition", "attachment; filename=" + zipFileName);
response.setContentLength((int) zipFile.length());
try {
FileInputStream fileInputStream = new FileInputStream(zipFile);
OutputStream responseOutputStream = response.getOutputStream();
int bytes;
while ((bytes = fileInputStream.read()) != -1) {
responseOutputStream.write(bytes);
}
} catch (IOException e) {
logger.error("Exception: " + e);
}
}
And the HTML:
<a class="btn" href="/path_to_servlet" target="_blank">Download zip</a>
Hope this helps!
So I found a hack for this : ) Just add ".zip" in your filename and set your content type as application/zip. Works like a charm.
response.setContentType("application/zip");
String licenseFileName = eId;
response.setHeader("Content-disposition", "attachment; filename=\"" + licenseFileName +".zip");

Firefox will not download this file as a CSV

I have tried everything I can think of. I have changed the mime type 100 times. Changed the headers 400 times. I've looked through stack over flow a dozen times. This works fine in Chrome. Soon as I go to download in Firefox it thinks it's a xlsx file, or a binary file. It even opens as an xlsx but it doesn't think it's a csv so the columns aren't seperated. If I save the file(instead of just hit open) it doesn't even put the extension on. I haven't even got to IE yet so this is kind of worrying me.
mime mapping
<mime-mapping>
<extension>csv</extension>
<mime-type>application/vnd.ms-excel</mime-type>
</mime-mapping>
I've tried text/csv, application/csv, application/binary, application/octet-stream.
public void doDownloadFile() {
PrintWriter out = null;
try {
String fileName = selectedPkgLine.getShortname() + ".csv";
HttpServletResponse response = (HttpServletResponse) FacesContext.getCurrentInstance().getExternalContext().getResponse();
HttpServletRequest request = (HttpServletRequest) FacesContext.getCurrentInstance().getExternalContext().getRequest();
response.setHeader("Pragma", "public");
response.setHeader("Expires", "0");
response.setContentType(request.getServletContext().getMimeType(fileName));
response.setHeader("Cache-Control", "must-revalidate, post-check=0, pre-check=0");
response.setHeader("Content-disposition", "attachment; filename=" + fileName + "");
response.setHeader("Content-Transfer-Encoding", "binary");
out = response.getWriter();
CSVWriter writer = new CSVWriter(out);
List<PkgLoad> pkgLoadList = pkgLoadService.findBetweenDates(selectedPkgLine, startDate, endDate);
List<String[]> stringList = new ArrayList<String[]>();
stringList.clear();
String[] header = {
"pkg_load_id",
"time_stamp",
"ounces",
"revolutions",
"wrap_spec_id",
"pkg_line_id"
};
stringList.add(header);
for (PkgLoad pkgLoad : pkgLoadList) {
String[] string = {
pkgLoad.getPkgLoadId().toString(),
pkgLoad.getTimeStamp().toString(),
pkgLoad.getOunces().toString(),
pkgLoad.getRevolutions().toString(),
pkgLoad.getWrapSpecId().getWrapSpecId().toString(),
pkgLoad.getPkgLineId().getPkgLineId().toString()
};
stringList.add(string);
}
response.setHeader("Content-length", String.valueOf(stringList.size()));
writer.writeAll(stringList);
out.flush();
} catch (IOException ex) {
Logger.getLogger(ViewLines.class.getName()).log(Level.SEVERE, null, ex);
} finally {
out.close();
}
}
Thanks for any help.
Safari, Opera and Chrome work fine. Haven't tried IE.
****EDIT****
Ok this entire time it was a spacing issue. My file name was "file name.csv" and this works in every browser except firefox. Soon as I put my file name to "filename.csv with no spaces it downloaded it find. I didn't notice that when it was downloading it was only downloading the first part of the name before the space. Good luck!
I had the same issue in PHP and found adding double quotes for the file name fixes the problem.
response.setHeader("Content-disposition", "attachment; filename=\"" + fileName + \"");
The content type text/csv is correct, but you should also add an charset encoding:
response.setHeader("Content-type: text/csv; charset=utf-8");
But what the hell is this:
response.setHeader("Content-Transfer-Encoding", "binary");
response.setHeader("Content-length", String.valueOf(stringList.size()));
Remove that headers! The content length is in bytes. Do not try to calculate it by yourself. It is definitly wrong in this example! A Mime-Type with major type text is not binary!
Ok this entire time it was a spacing issue. My file name was "file name.csv" and this works in every browser except firefox. Soon as I put my file name to "filename.csv with no spaces it downloaded it find. I didn't notice that when it was downloading it was only downloading the first part of the name before the space.
In the future make sure the filename has a single quote around it in the header. This will allow Firefox to download it correctly(without stripping off anything past the space) if you need a space the file name.
Good luck!
Add the content-type header with value text/csv
response.setHeader("Content-type: text/x-csv");
Response.AddHeader("content-disposition", string.Format("attachment;filename=\"{0}\"", fileName));
Response.ContentType = "text/csv; charset=utf-8";
I am not expert in Java/JSP but are you sure this is correct for text/csv?
response.setHeader("Content-Transfer-Encoding", "binary");
Did you try commenting out this? In PHP I will simply echo/print the CSV preceded with headers content-type headers and disposition type.

Categories

Resources