I have a Spring REST application, one of the end-points is a download link, where the downloaded file is generated on the fly.
It all works except the filename is wrong.
Here's the relevant parts of the controller:
#RestController
#RequestMapping("/export")
public class ExportREST {
#RequestMapping(method=RequestMethod.GET)
public void export(HttpServletResponse response) throws Exception {
//stuff omitted...
writeCsvResponse(response);
}
private void writeCsvResponse(HttpServletResponse response) throws IOException {
String fileName = "db.export."+dateFormat.format(new Date());
response.setContentType( "application/octet-stream" );
response.setHeader( "Content-Disposition:", "attachment;filename=" + "\"" + fileName + "\"" );
//write stuff to response...
response.setContentLength(totalLength);
response.setBufferSize(1024);
response.flushBuffer();
pout.close();
}
}
So, what I want is a filename with a generated timestamp, but actually the filename is always export, presumably it's getting it from the URL.
Have I missed something?
There's a colon at the end of "Content-Disposition:". Without it the filename should be picked up.
Maybe this help you
if (mimeType == null) {
// set to binary type if MIME mapping not found
mimeType = "application/octet-stream";
}
System.out.println("MIME type: " + mimeType);
// set content attributes for the response
response.setContentType(mimeType);
More detail servlet in : https://stackoverflow.com/questions/41914092/how-change-servlet-which-download-single-file-but-can-folderfew-files-in-fold
Related
I have a small problem which I've been unable to solve for a few hours. I am basically trying to stream an excel file from the resources folder withing a jar. The file has around 9KB in my file manager, however, when I download it by visiting the REST endpoint I receive a 13/14KB file which can no longer be opened by excel. The metadata is set correctly, as is the filename. I suspect the streaming/copying process is somehow corrupting the file. Here you may see the code snippet:
public void getTemplateByDataType(HttpServletResponse response, DataType dataType) {
String fileName = "excel_template.xlsx";
String templateDirectory = "templates";
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
response.setHeader(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + fileName + "\"");
InputStream data = this.getClass().getClassLoader().getResourceAsStream(templateDirectory + "/" + fileName); // loading file from resources folder
try {
IOUtils.copy(data, response.getOutputStream()); // copying to httpservletresponse output stream
} catch (IOException e) {
//...
}
}
I've already tried reading from a simple text file in the same location in order to verify whether the getResourceAsStream call works and this is the case. So I am somehow breaking something with the IOUtils.copy I guess? Does anybody have any suggestions why this simple code snippet breaks my xlsx files?
Just to get the full picture, the controller is relatively simple:
#GetMapping(value = "/templates", produces = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
public #ResponseBody
void getFileTemplate(HttpServletResponse response,
#ApiParam(value = "Type of data import", required = true) #RequestParam String dataType) {
importService.getTemplateByDataType(response, DataType.fromValue(dataType));
}
Try Streaming Output. Maybe this would help you Example of using StreamingOutput as Response entity in Jersey
If you want to download as an attachment, then return the Response like below:
Response.status(Response.Status.OK)
.header(HttpHeaders.CONTENT_DISPOSITION,
String.format("attachment; filename=\"download.gz\""))
.entity(streamingOutput)
.build();
StreamingOutput streams the content of the file and at the client end, it will be downloaded as an attachment.
try to copy this file directly to your output stream.
#GetMapping(value = "/templates", produces = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
public #ResponseBody
void getFileTemplate(HttpServletResponse response,
#ApiParam(value = "Type of data import", required = true) #RequestParam String dataType) {
String fileName = "excel_template.xlsx";
String templateDirectory = "templates";
Path templateFilePath = Paths.get(getClass().getClassLoader().getResource(templateDirectory + "/" + fileName).toURI());
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
response.setHeader(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + fileName + "\"");
try {
FileCopyUtils.copy(new BufferedInputStream(new FileInputStream(templateFilePath.toFile())), response.getOutputStream());
response.getOutputStream().flush();
response.getOutputStream().close();
} catch (IOException e) {
//...
}
}
```
I have the following code for a method thats supposed to return a file for download. The path variables are used to navigate to a specific file on the filesystem.
#GetMapping("/files/{username}/{docId}/{revisionNo}/{filename:.}")
#ResponseBody
public ResponseEntity<Resource> serveFile (#PathVariable("username") String username,
#PathVariable("docId") String docId,
#PathVariable("revisionNo") String revisionNo,
#PathVariable("filename") String filename)
{
System.out.println("Serve file firing"); // for debug
String filepath = username + "/" + docId + "/" + revisionNo + "/" + filename;
Resource file = storageService.loadAsResource(filepath); // this method works fine
return ResponseEntity
.ok()
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\""+file.getFilename()+"\"")
.body(file);
}
However when I start the server and go to "http://localhost:8080/files/admin/0/0/Capture1.PNG" I get a 404 error and no download. The debug println on line 9 doesn't print which would indicate this method is not being triggered.
For context this similar code does work correctly although it doesn't use the filesystem hierarchy i need and only returns files in the root folder.
#GetMapping("/files/{filename:.+}")
#ResponseBody
public ResponseEntity<Resource> serveFile(#PathVariable String filename) {
Resource file = storageService.loadAsResource(filename);
return ResponseEntity
.ok()
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\""+file.getFilename()+"\"")
.body(file);
}
I think then it must be to do with how I've used the path variables but all the research I've done indicates that its correct...running out of time to fix please help!
i want to make a rest api controller (spring boot) that when petitioned with a get will allow me to download an excel file. currently i have this endpoint:
#RequestMapping(value = "/download.xls", method = RequestMethod.GET)
public ResponseEntity Survey_Reports(#RequestParam(value = "evaluated") String evaluated){
return surveyService.getSurveysFile(evaluated);
}
wich ultimately calls to this method:
public static ResponseEntity getDownloadResponse() {
File file2Upload = new File("Survey_Reports.xls");
Path path = Paths.get(file2Upload.getAbsolutePath());
ByteArrayResource resource = null;
try {
resource = new ByteArrayResource(Files.readAllBytes(path));
} catch (IOException e) {
logger.error("there was an error getting the file bytes ", e);
}
return ResponseEntity.ok()
.contentLength(file2Upload.length())
//this line doesnt seem to work as i set the file format in the controller request mapping
.contentType(MediaType.parseMediaType("application/vnd.ms-excel"))
.body(resource);
}
everything seems to work semi-fine as i get the download.xls(as the mapping) file correclty, but now i want to make the downloaded file have some specific name like: evaluatedName.xls or userDateEndDate.xls or some other stuff, is there a way to edit the response entity to do so? so that i dont have to name the mapping "download.xls"
In context of HttpServletResponse response you can do this like this
response.setContentType("application/csv");
response.setHeader("Content-Disposition", "attachment; filename=" + csvName);
and for ResponseEntity i assume you can use something like this:
ResponseEntity.ok().header("Content-Disposition","attachment; filename=" + csvName );
I am new to the mockmvc api.
I am trying to write units for my controllers and one of the method performs download. Please find the code snippet:
Controller :
#RequestMapping(value = "/download-template", method = RequestMethod.GET, produces = MediaType.APPLICATION_OCTET_STREAM_VALUE)
#ResponseBody
public FileSystemResource downloadTemplate(HttpServletRequest request,
HttpServletResponse response) {
logger.info("User ID: " + request.getAttribute("userId")
+ " - POST: /upload/download-template");
String rootDir = config.getBaseFolder();
DownloadUploadTemplateResponse res = uploadService.downloadTemplate(
rootDir, false);
response.setHeader("Content-Disposition",
"attachment; filename=" + res.getFileName());
return new FileSystemResource(res.getTemplateFile());
}
Unit test code:
#Test
public void testDownloadTemplate(){
DownloadResponse res = new DownloadResponse();
String templateFile = "upload-template.xlsx";
String fileName = System.getProperty("java.io.tmpdir") + templateFile;
res.setFileName(fileName);
res.setTemplateFile(templateFile);
when(config.getBaseFolder()).thenReturn(System.getProperty("java.io.tmpdir"));
when(uploadService.downloadTemplate( System.getProperty("java.io.tmpdir"), false)).thenReturn(res);
//File file = new File(fileName);
FileSystemResource resource = new FileSystemResource(res.getTemplateFile());
try{
ResultActions action = mockMvc.perform(get("/upload/download-template").contentType(APP_OCTET_STREAM_VALUE_UTF8));
action.andExpect(header().string("Content-Disposition",
"attachment; filename=" + res.getFileName()));
action.andExpect(status().isOk());
}catch(Exception e){
e.printStackTrace();
fail();
}
}
Below line throws java.io.FileNotFoundException: upload-template.xlsx (The system cannot find the file specified):
ResultActions action = mockMvc.perform(get("/upload/download-template").contentType(APP_OCTET_STREAM_VALUE_UTF8));
Please guide what additional I need to add in the httprequestbuilder to resolve this issue.
My bad .. the error was clear enef to fix the issue . The problem was the file was not physically present :).
So by placing the file in the temp folder helped me resolve the issue. Feel like ahhh .
Hi have written controller class like below. I am trying to get file from mongo db and try to download it.
organizationFileAttachmentService.setUser(getUser());
GridFSDBFile file = organizationFileAttachmentService.getGridFSDBFileById(new ObjectId(id), "File");
if (file != null) {
byte[] content = organizationFileAttachmentService.findByIdAndBucket(new ObjectId(id), "File");
try {
int size = content.length;
InputStream is = null;
byte[] b = new byte[size];
try {
is = new ByteArrayInputStream(content);
is.read(b);
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (is != null)
is.close();
} catch (Exception ex) {
}
}
response.setContentType(file.getContentType());
// String attachment =
// "attachment; filename=\""+file.getFilename()+"\"";
String attachment = "attachment; filename=" + file.getFilename();
// response.setContentLength(new
// Long(file.getLength()).intValue());
response.setCharacterEncoding(file.getMD5());
response.setHeader("content-Disposition", attachment);// "attachment;filename=test.xls"
// copy it to response's OutputStream
// FileCopyUtils.copy(is, response.getOutputStream());
IOUtils.copy(is, response.getOutputStream());
response.flushBuffer();
is.close();
} catch (IOException ex) {
_logger.info("Error writing file to output stream. Filename was '" + id + "'");
throw new RuntimeException("IOError writing file to output stream");
}
but i am not able to down load file. can any one help me.
In case you missed it, Spring provides various built in resource handlers.
http://docs.spring.io/spring/docs/3.2.5.RELEASE/spring-framework-reference/html/resources.html#resources-implementations
If your method returns one of those (perhaps the ByteArrayResource in your case), then you just need a couple of annotations on the interface like so:
#RequestMapping(value = "/foo/bar/{fileId}",
method = RequestMethod.GET,
produces = { MediaType.APPLICATION_OCTET_STREAM_VALUE })
#ResponseBody FileSystemResource downloadFile(Long fileId);
No fiddling with encodings and headers for you that way. I'd recommend trying that before rolling your own.
Edit: The above worked fine in Spring 3.1.4. It no longer works for 3.2.x or 4.x. Whereas previously, the produces = { MediaType.APPLICATION_OCTET_STREAM_VALUE } would cause Spring to add the appropriate headers, it now treats that as a restriction. If accessing the URL with a standard web browser, an accept header of "application/octet-stream" will not be sent. Spring will therefore return a 406 error. To get it working again, such a method needs to be re-written without the "produces" attribute. Instead, add HttpServletResponse to the method arguments and add the header inside the method. i.e.:
#RequestMapping(value = "/foo/bar/{fileId}",
method = RequestMethod.GET)
#ResponseBody FileSystemResource downloadFile(
Long fileId, HttpServletResponse response) {
...
response.setHeader( "Content-Disposition", "attachment;filename=" + fileName );
...
}
Edit redux:
Now using Spring 4.0.7 via Spring Boot 1.1.8. It would appear that setting the produces = { MediaType.APPLICATION_OCTET_STREAM_VALUE } instruction is now working again. Just having that instruction seems to be enough for all the browsers I have tried. Note however, that I have also found that it does not set the Content-Disposition, which is left as application/json. Although this doesn't seem to be an issue for browsers, I have come across bugs in PHP client applications, which seem to behave only based on the Content-Disposition. So it seems that the current solution is to do both of the above!
I have changed my request as GET and added request in anchor tag in html. Aslo changed my code as
#RequestMapping(value = "/getFileById/{id}", method = RequestMethod.GET)
public #ResponseBody
void download(#PathVariable String id, HttpServletRequest request, HttpServletResponse response) throws IOException {
organizationFileAttachmentService.setUser(getUser());
GridFSDBFile file = organizationFileAttachmentService.getGridFSDBFileById(new ObjectId(id), "File");
if (file != null) {
try {
response.setContentType(file.getContentType());
response.setContentLength((new Long(file.getLength()).intValue()));
response.setHeader("content-Disposition", "attachment; filename=" + file.getFilename());// "attachment;filename=test.xls"
// copy it to response's OutputStream
IOUtils.copyLarge(file.getInputStream(), response.getOutputStream());
} catch (IOException ex) {
_logger.info("Error writing file to output stream. Filename was '" + id + "'");
throw new RuntimeException("IOError writing file to output stream");
}
}
}
Now it is working fine for me.