Download a file from HTML Page Using Spring - java

I want to download a file from from the front end. The file is generated on TestFlow.generateReport
My runs until the end but it doesn't download anything. What am I missing here??
#RequestMapping(value = "/flow/generate-report" , method = RequestMethod.GET)
public #ResponseBody void generateFlowReport(#RequestParam("flowName") String flowName, HttpServletResponse response) {
InputStream resource = TestFlow.generateReport(flowName);
response.setContentType("application/force-download");
response.setHeader("Content-Disposition","attachment; filename=report-" + flowName + ".xlsx");
try {
IOUtils.copy(resource,response.getOutputStream());
response.flushBuffer();
resource.close();
} catch (IOException e) {
e.printStackTrace();
}
}

The original AJAX request can not handle file download request.
You must create a virtual form. see this question. And there are lots of jQuery plugin help you to do this.
And since its a download request, your controller method should not use #ResponseBody, take over the response all by yourself, close it after writing the content.

Related

Why does Chrome closes connection when REST API returns a large response?

I use this REST API
#GET
#Path("/get-file")
#Consumes(MediaType.APPLICATION_JSON + ";charset=UTF-8")
public void getFile(#Context HttpServletRequest request, #Context HttpServletResponse response,
#QueryParam("filePath") String filePath, #QueryParam("fileName") String fileName) throws IOException {fileName);
File fileToDownload = new File(filePath);
if (fileToDownload.exists()) {
java.nio.file.Path path = Paths.get(fileToDownload.toURI());
response.setContentType(MediaType.APPLICATION_OCTET_STREAM);
response.setHeader(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=" + fileName);
try {
response.setHeader(HttpHeaders.CONTENT_LENGTH, Long.toString(Files.size(path)));
Files.copy(path, response.getOutputStream());
response.flushBuffer();
} catch (IOException e) {
log.error(e.getLocalizedMessage(), e);
throw e;
}
} else {
throw new FileNotFoundException();
}
}
to download files generated from the server. It all works fine if the file i'm trying to get is less than 50GB (more or less), but when I try to download a 50GB while using Chrome, I get a java.net.SocketException: Connection reset exception. This doesn't happen in Firefox, and I can get the file as usual.
This is what I see in the Chrome Network Tab:
Downloading the file is the only operation I do and it the fails immediately.
If I omit the CONTENT_LENGTH header from the response, the size of the response reaches 48GB~ and then I get the same error.
Does anyone have any clue about this? On server side I use Java 7 (I tried with Java 8 too) and on client side I use AngularJS v1.5.11. I use ApacheTomcat to deploy my application. Let me know if you need more info about the problem.

How to write a Rest API in spring boot to download an excel sheet that is present in the resource?

I want to re-write a legacy code in spring boot. When the api is hit, the excel file in the resource should be downloaded. The following is the part of MVC code using JSP Servlet.
#RequestMapping(value = "/downloadTemplate", method = RequestMethod.GET)
public void doDownload(HttpSession session,
HttpServletResponse response) throws IOException {
String fn = "/resources/files/censusTemplates";
try {
InputStream inputStream = servletContext.getResourceAsStream(fn);
response.setContentType("application/xlsx");
response.setHeader("Content-Disposition", "attachment; filename=SimpleStandardTemplate.xlsm");
FileCopyUtils.copy(inputStream, response.getOutputStream());
inputStream.close();
} catch (Exception e) {
logger.debug("An error occurred while trying to downloadTemplate: {}", e.getMessage());
}
}
Please help me to implement in Spring boot.

GWT Download Excel .xlsx gives me a corrupted file

I'm working on a GWT application which gives every team in the company an overview about what they have to do.
The program is working, but now we want that the Excel table which you can download will be a .xlsx and not a .xls.
This whole project is new for me and I consider myself as a beginner in GWT.
In the code, when the filename is given for the Excel table, there is a +".xls" at the end. When I change it to +".xlsx" and test the application, the download still works. However, when I try to open the file in Excel, it shows me an error message and tells me the file is corrupted. (.xls works)
Can you explain to me how a download works in GWT with a serverSite generated Excel?
Maybe you have some ideas what causes the file to be corrupted
(sadly the programmer of this application is on holiday, so I cannot ask him)
public class Download extends HttpServlet {
private static final long serialVersionUID = 5580666921970339383L;
#Override
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
#Override
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String filename = (String)request.getSession().getAttribute(CrossReportConstants.ATTR_FILENAME);
byte[] data = (byte[])request.getSession().getAttribute(CrossReportConstants.ATTR_REPORT);
request.getSession().setAttribute(CrossReportConstants.ATTR_FILENAME, null);
request.getSession().setAttribute(CrossReportConstants.ATTR_REPORT, null);
response.setContentType("application/vnd.ms-excel");
response.setHeader("Content-Disposition", "attachment;filename=" + filename);
response.setHeader("Pragma", "no-cache");
response.setHeader("Expires", "0");
response.setHeader("Cache-Control", "no-cache, no-store, must-revalidate");
response.setContentLength(data.length);
try {
InputStream in = new ByteArrayInputStream(data);
ServletOutputStream out = response.getOutputStream();
byte[] outputByte = new byte[4096];
// copy binary contect to output stream
while (in.read(outputByte, 0, 4096) != -1) {
out.write(outputByte, 0, 4096);
}
in.close();
out.flush();
out.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
Now as you provided code you question can be easily answered:
The shown code defines a HttpServlet. Somewhere in your project there is a file called web.xml. In this file the class you showed is mapped to an url pattern, therefore you server knows that a specific url should be handled by this servlet.
The servlet you showed first extracts the file name and the file content out of the session. Additional the http response is prepared and the file content is written out. Now you just have to replace the content type of the response with the one for xlsx.
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
The browser which handles the http response now recognizes the download as a .xlsx file. The file extension does not really matter in this step, as you noticed.
When the original programmer of the servlet comes back from his hollidays, you should/could recommend him to use response.sendError() (with an appropriate http status code) instead of e.printStackTrace(). Then the user of the servlet can better understand if something do not work and who is to blame.

Download or redirect with error message to another controller action in Spring web MVC

Idea: I have a Spring web MVC action that should accomplish one or both of these taks:
Download a file from a remote server and write the input stream to the response output stream
Or catch the exception of the download, set one of multiple error messages and redirect to the /addresses page. The address page will display the error
Problem: Spring is unable to download a file and redirect in case of a problem - somehow flash attributes don't work because the get lost in the redirect:
#ResponseBody
#RequestMapping(value = "/download/{fileaddress}", method = RequestMethod.GET)
public void download(HttpServletRequest request, HttpServletResponse response, #PathVariable(value = "fileaddress") String fileaddress) throws Exception
{
if(fileaddress != null && fileaddress.length() > 0)
{
try
{
// Get the remove file based on the fileaddress
RemoteFile remotefile = new RemoteFile(fileaddress);
// Set the input stream
InputStream inputstream = remotefile.getInputStream();
// Write the input stream to the output stream or throw an exception
Utils.writeTo(inputstream, response.getOutputStream());
}
catch(MyExceptionA)
{
// TODO: Define error message a and pass it to /addresses
// PROBLEM: Flash attributes that contain all critical error information don't work
response.sendRedirect(request.getContextPath() + "/addresses");
}
catch(MyExceptionB)
{
// TODO: Add another error message and redirect
response.sendRedirect(request.getContextPath() + "/addresses");
}
catch(MyExceptionC)
{
// TODO: Add another error message and redirect
response.sendRedirect(request.getContextPath() + "/addresses");
}
catch(MyExceptionN)
{
// TODO: Add another error message and redirect
response.sendRedirect(request.getContextPath() + "/addresses");
}
}
else
{
// TODO: Add error message
response.sendRedirect(request.getContextPath() + "/addresses");
}
}
JSP page of /addresses:
<%# page pageEncoding="UTF-8" %>
<%# taglib prefix="tags" tagdir="/WEB-INF/tags" %>
<%# taglib prefix="core" uri="http://java.sun.com/jsp/jstl/core" %>
<tags:index>
<jsp:attribute name="content">
<core:if test="${not empty error}">
<div class="alert alert-danger">
<p>${error}</p>
</div>
</core:if>
<p>Page under construction!</p>
</jsp:attribute>
</tags:index>
Question: How I am able to display the error message (Simple string for example) in the /addresses site? Working with different URL parameter (error=errora, error=errorb ...) is a huge pain, if there are multiple error types and passing the error message as GET parameter looks unprofessional and is the root of encoding problems.
What you need is the RedirectAttributes a specialization of the Model which controllers can use to select attributes for a redirect scenario.
So for a working example see below code:
#ResponseBody
#RequestMapping(value = "/download/{fileaddress}", method = RequestMethod.GET)
public Object download(#PathVariable(value = "fileaddress") String fileaddress, RedirectAttributes redirectAttrs) throws Exception {
if(StringUtils.hasText(fileaddress)){
try{
// Get the remove file based on the fileaddress
RemoteFile remotefile = new RemoteFile(fileaddress);
// Set the input stream
InputStream inputstream = remotefile.getInputStream();
// asume that it was a PDF file
HttpHeaders responseHeaders = new HttpHeaders();
InputStreamResource inputStreamResource = new InputStreamResource(inputStream);
responseHeaders.setContentLength(contentLengthOfStream);
responseHeaders.setContentType(MediaType.valueOf("application/pdf"));
return new ResponseEntity<InputStreamResource> (inputStreamResource,
responseHeaders,
HttpStatus.OK);
} catch (MyExceptionA | MyExceptionB | MyExceptionC | MyExceptionD ex) {
redirectAttrs.addFlashAttribute("error", ex.getMessage());
}
} else {
redirectAttrs.addFlashAttribute("error", "File name is required");
}
return "redirect:/addresses";
}
Update: I've thought the question is about situations where RedirectAttributes are not available because otherwise, the solution is pretty obvious (use RedirectAttributes).
Orignal answer:
I'm using the following code to bind messages to the flash map in situations where Spring doesn't support RedirectAttributes (e.g. in ExceptionHandler methods):
public static Feedback getInstance(HttpServletRequest request, HttpServletResponse response) throws IllegalArgumentException {
FlashMap flashMap = RequestContextUtils.getOutputFlashMap(request);
Object o = flashMap.get(KEY);
if (o != null) {
if (o instanceof Feedback) {
return Feedback.class.cast(o);
} else {
throw new IllegalArgumentException(...);
}
} else {
FeedbackContainer feedbackContainer = new FeedbackContainer();
flashMap.put(KEY, feedbackContainer);
FlashMapManager flashMapManager = RequestContextUtils.getFlashMapManager(request);
flashMapManager.saveOutputFlashMap(flashMap, request, response);
return feedbackContainer;
}
where Feedback / FeedbackContainer is a container for messages which is then accessed in JPSs via JSON serialization. In your case you may use a mere String with key "error" and access it directly in the JSP:
void storeErrorMsg(HttpServletRequest request, HttpServletResponse response, String message) {
FlashMap flashMap = RequestContextUtils.getOutputFlashMap(request);
flashMap.put("error", message);
FlashMapManager flashMapManager = RequestContextUtils.getFlashMapManager(request);
flashMapManager.saveOutputFlashMap(flashMap, request, response);
}
The main reason for using my own message container is the possibility to have multiple messages with different levels and additional getInstance methods for RedirectAttributes, Model or ModelMap, so I don't have to care about duplicate feedback bindings and/or the different binding code.
i can't explain about why it is not downloading a file from server but coming to your second query , you can redirect in two ways either take your method return type as ModelAndView and do like this
ModelAndView mav = new ModelAndView("write here your jsp page name even is will work for remote server");
try {
mav.addObject("some key","here you can write any message that will you can show in your jsp page..");
then return mav.
like this you can redirect how many pages you want with if condition.
and second approach you can take your method return type String and you can directly redirect to your required jsp.
I would suggest the following approach:
Since you have some remote file as an input stream you cannot read it from the point where an exception occurred. Hence you can decrease error chances with first downloading the file to a temp folder on local host.
Here if there is an error, you can either redirect as you suggested, or just make another attempt and redirect after several failed attempts. This way you are not writing anything to the stream until you are sure the file downloaded successfully.
Once the file is downloaded to the server you can pass it to the client. For instance you can read bytes and save current position. Suppose there is an exception in the middle of writing to the stream. You can restore writing from failed point reading from the middle.
A small example
...
final String localFileName = saveFileLocally(fileaddress)
if (localFileName == null) {
response.sendRedirect(request.getContextPath() + "/addresses");
} else {
safeWrite(new BufferedInputStream(new FileInputStream(new File(localFileName))), response.getOutputStream());
}
...
safeWrite should support local file read exception handling with reading from the file middle. There should be standard java api for such operations.

Spring MVC Image Response can not be copied and pasted in Word and other applications

I have an image saved as a blob in a database. I send this image with a Spring MVC controller to the browser. That part works well with the following code:
#RequestMapping(value = "/img/{id}", method = RequestMethod.GET)
public void getImage(#PathVariable("id") Long id, HttpServletResponse response) {
Image image = imageRepository.findOne(id);
if (image != null) {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.IMAGE_PNG);
return new ResponseEntity<>(image.getData(), headers, HttpStatus.CREATED);
}
return null;
}
But when i try to copy the image and paste it in, for example, Word, there is just a blank square. I tried to paste the image in Paint.NET and there it works again.
I tried some other methods of sending the image to the browser, for example:
#RequestMapping(value = "/img/{id}", method = RequestMethod.GET)
public void getImage(#PathVariable("id") Long id, HttpServletResponse response) {
Image image = imageRepository.findOne(id);
if (image != null) {
ByteArrayInputStream bis = new ByteArrayInputStream(image.getData());
response.reset();
response.setContentLength(image.getData().length);
response.setContentType("image/png");
response.setStatus(HttpServletResponse.SC_OK);
try {
OutputStream outputStream = response.getOutputStream();
IOUtils.copy(bis, outputStream);
bis.close();
outputStream.flush();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
This works also well with browser but copy and paste in applications like word does also not work. I'm clueless what i can try or where the mistake could be.
Saving the Image and inserting it in the application works but is unfortunately not an option.
Does anyone know what else i can try or what could go wrong?
Thanks in advance!
Ok i solved it. The problem in this case was actually the SSL-Connection forced by the Server. I set up a SecurityConstraint for the URL-Path for the images that excludes it from using SSL and now it works.
Edit: I'm still confused why copy and paste worked in Paint.NET but not in Word...

Categories