How can I rename the response from an http request? - java

I have a relatively simple Java/Tomcat requirement but I can't figure out how to implement it.
I have an HTTPS POST request into a specific server URL, call it /myserver/somecontroller.do.
The controller method correctly returns an Excel document with a mime type of application/vnd.ms-excel.
This all works fine. The problem is that the response gets downloaded on the client computer with the name somecontroller.do instead of something that Excel understands, like myspreadsheet.xls.
So when the user clicks the file to open it, they get an ugly error message instead of the spreadsheet they were expecting, and I get a trouble ticket in my inbox.
They could rename the file, but the optimal solution is to have the system name the file correctly.
How can I modify the HTTPServletResponse parameters so that it understands it's sending a document called myspreadsheet.xls instead of somecontroller.do?
Some cleaned-up example code:
// Returns a spreadsheet, but it's called "somethingelse.do" instead of
// "somethingelse.xls"
#RequestMapping(value="/something/somthingelse.do", method=RequestMethod.POST)
public String doSomething(#ModelAttribute ReportModel reportModel, BindingResult bindResult,HttpServletResponse response,Model model) throws Exception{
Workbook reportObj = ReportService.generateExcel(ReportService.getReportData(reportModel.getReportParameters()));
response.setContentType("application/vnd.ms-excel");
OutputStream out = response.getOutputStream();
HSSFWorkbook workbook = (HSSFWorkbook) reportObj;
workbook.write(out);
out.flush();
return SomeViewMapping.VIEW_SOMETHING.getViewName();
}

You need one more header. Something like:
response.setHeader("Content-Disposition", "attachment; filename=myspreadsheet.xls");

Related

Using ServletOutputStream and PrintWriter in the same response [duplicate]

I want to redirect to a page after writing the excel file. The servlet code is given below:
ByteArrayOutputStream outByteStream = new ByteArrayOutputStream();
workbook.write(outByteStream);
byte [] outArray = outByteStream.toByteArray();
response.setContentType("application/ms-excel");
response.setContentLength(outArray.length);
response.setHeader("Content-Disposition", "attachment; filename=name_"+date+".xlsx");
response.setIntHeader("Refresh", 1);
OutputStream outStream = response.getOutputStream();
outStream.write(outArray);
response.sendRedirect("url/reports.jsp");
This code downloads an Excel file which i have created.
when i call the above servlet, the excel file is being downloaded but it is throwing following exception in the last line :
Servlet Error: ::java.lang.IllegalStateException: Cannot call sendRedirect() after the response has been committed
Hence i am unable to redirect to a new page. what can i do to access the response object after i write the output in "outStream"
The basic problem is that this ...
I want to redirect to a page after writing the excel file.
... describes two separate responses. The server cannot chain them together by itself because the client will expect only one response to each request. Because two requests are required to elicit two responses, automation of this sequence will require client-side scripting.
Personally, I would probably put the script on the front end: a handler on the appropriate button or link that first downloads the file and then (on success) issues a request for the new page. It would also be possible to do as suggested in comments, however: put script in the new page that downloads the file.
You cannot have a body with a redirect because the browser, when receiving a redirect, will issue a second request to the URL it has found (in header Location), and it's the response of that second request that is displayed, unless it is also a redirect, in which case, it will issue a third request, and so on...

Java servlet method call squence lead to different result

I use SpringBoot 2.1.1 with Java 11,when i write a http interface, i need to return a excel file to client, and i fill the file with Java code, then use HttpServletResponse.getOutputStream().write() to send the file.The puzzle thing is first call HttpServletResponse.getOutputStream().write() then call HttpServletResponse.setContentType(), the client cannot accept the content-type. where is the issue?
SpringBoot 2.1.1 with Java 11.
case 1:
byte[] errorFile = new byte[0];
// fill errorFile...
ServletOutputStream out = response.getOutputStream();
out.write(errorFile);
response.setHeader("Error-File", "True");
response.setContentType("application/vnd.ms-excel");
In this call sequence, if errorFile has a short content, it will be fine, server will write content-type and the header(Error-File) to clients, but if the errorFile has a long content, clients cannot get the content-type and the header(Error-File).
case 2:
byte[] errorFile = new byte[0];
// fill errorFile...
response.setHeader("Error-File", "True");
response.setContentType("application/vnd.ms-excel");
ServletOutputStream out = response.getOutputStream();
out.write(errorFile);
If i use this call sequence, it will be fine in any length of errorFile.
The issue is that an HTTP response contains
the status,
then the headers,
then the body.
So if you first write the body, it's too late to write the headers.

Return a ModelAndView Spring MVC PDF Object as a saveable attachment

I do not have much Spring MVC experience. I inherited some RESTful controller code that finds a record in a database and uses
org.springframework.web.servlet.view.jasperreports.JasperReportsPdfView to create a PDF view of the record.
Here's the method:
#RequestMapping(value = "/profile/{uniqueId}.pdf", method = RequestMethod.GET)
#ResponseBody
public ModelAndView getProfilePdf(HttpServletRequest request, #PathVariable("uniqueId") String uniqueId {
Profile profile = getProfileByUniqueId(request, uniqueId);
Map<String, Object> reportData = buildReportData(profile);
return new ModelAndView(profileReport, reportData);
}
There are lots of supporting objects and methods but since I cannot cut and paste the code in, I'll leave them out for now.
Here's the problem. When the URL is called the PDF is generated and it pops up in the browser. If I edit my browser preferences I can get the PDF to pop up in Adobe Acrobat. But what I NEED is for the popup window that asks you if you want to open or save the file to appear.
I believe the only way to make that happen is to set the Content-disposition to an attachment.
So I added "HttpServletResponse response" to the method parameters and added this to the code:
response.setHeader("Content-disposition", "attachment; filename=profile.pdf");
No luck. I also tried adding a content type:
response.setContentType("application/pdf");
Still no luck.
I need to learn how to send back a ModelAndView object as a file attachment, or maybe convert it to a byte stream and manually stream it back?? No clue here. Any thoughts would be greatly appreciated.
By default the JasperReportsPdfView sets the Content-Disposition to inline but only if the header isn't preconfigured as something else.
The JasperReportsPdfView has a property named headers in which you can configure, or override, headers to send along with the generated PDF. If you would include the Content-Disposition header in this you can set it to something else, in your case attachment.
<bean id="yourreport" class="org.springframework.web.servlet.view.jasperreports.JasperReportsPdfView">
<property name="headers">
<map>
<entry key="Content-Disposition" value="attachment; profile.pdf" />
</map>
<property>
</bean>
Something like this should do the trick (Instead of a map you could also use a props element).
what you need to do is to stream the PDF file's bytes directly to the output stream and flush the response. In Spring you can do this like this:
#RequestMapping(value="/displayProcessFile/{processInstanceId}", method=RequestMethod.GET)
public ResponseEntity<byte[]> displayProcessFile(#PathVariable String processInstanceId) throws UnauthorizedUserAccessException{
Document processFile=null;
try {
processFile = documentService.retrieveProcessFile(Long.parseLong(processInstanceId));
} catch (ProcessFileNotFoundExpection e) {
e.printStackTrace();
}
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.parseMediaType("application/pdf"));
headers.add("content-disposition", "inline;filename=" + processFile.getDocName());
ResponseEntity<byte[]> response = new ResponseEntity<byte[]>(processFile.getContent(), headers, HttpStatus.OK);
return response;
}
Maybe you have to change your browser settings, so that it will ask every time before opening the file directly.
Please find the link: http://malektips.com/google-chrome-prompt-download-file.html#.VnLAgbiGRHw
Also you will have to set:
response.setContentType("application/download");
response.setHeader("Content-disposition", "attachment; filename=profile.pdf");

Jersey servlet returns zip file that contains more bytes than response sent

I'm trying to implement a simple servlet that returns a zip file that is bundled inside the application (simple resource)
So I've implemented the following method in the server side:
#GET
#Path("{path}/{zipfile}")
#Produces("application/zip")
public Response getZipFile(
#PathParam("path") String pathFolder,
#PathParam("zipfile") String zipFile) IOException {
String fullPath= String.format("/WEB-INF/repository/%s/%s",
pathFolder, zipFile);
String realPath = ServletContextHolder.INSTANCE.getServletContext()
.getRealPath(fullPath);
File file = new File(realPath );
ResponseBuilder response = Response.ok((Object) file);
return response.build();
}
When I call this method from the borwser, the zip file is downloaded and its size is the same number of bytes as the original zip in the server.
However, when I call this using a simple XMLHttpRequest from my client side code:
var oXHR = new XMLHttpRequest();
var sUrl = "http://localhost:8080/path/file.zip"
oXHR.open('GET', sUrl);
oXHR.responseType = 'application/zip';
oXHR.send();
I can see in the Network tab of the Developer tools in chrome that the content size is bigger, and I'm unable to process this zip file (for instance JSzip doesn't recognize it).
It seems like somewhere between my response and the final response from org.glassfish.jersey.servlet.ServletContainer, some extra bytes are written/ some encoding is done on the file.
Can you please assist?
Best Regards,
Maxim
When you use an ajax request, the browser expects text (by default) and will try to decode it from UTF-8 (corrupting your data).
Try with oXHR.responseType = "arraybuffer"; : that way, the browser won't change the data and give you the raw content (which will be in oXHR.response).
This solution won't work in IE 6-9 : if you need to support it, check JSZip documentation : http://stuk.github.io/jszip/documentation/howto/read_zip.html
If it's not the right solution, try downloading directly the zip file (without any js code involved) to check if the issue comes from the js side or from the java side.

Download dynamic file with GWT

I have a GWT page where user enter data (start date, end date, etc.), then this data goes to the server via RPC call. On the server I want to generate Excel report with POI and let user save that file on their local machine.
This is my test code to stream file back to the client but for some reason I think it does not know how to stream file to the client when I'm using RPC:
public class ReportsServiceImpl extends RemoteServiceServlet implements ReportsService {
public String myMethod(String s) {
File f = new File("/excelTestFile.xls");
String filename = f.getName();
int length = 0;
try {
HttpServletResponse resp = getThreadLocalResponse();
ServletOutputStream op = resp.getOutputStream();
ServletContext context = getServletConfig().getServletContext();
resp.setContentType("application/octet-stream");
resp.setContentLength((int) f.length());
resp.setHeader("Content-Disposition", "attachment; filename*=\"utf-8''" + filename + "");
byte[] bbuf = new byte[1024];
DataInputStream in = new DataInputStream(new FileInputStream(f));
while ((in != null) && ((length = in.read(bbuf)) != -1)) {
op.write(bbuf, 0, length);
}
in.close();
op.flush();
op.close();
}
catch (Exception ex) {
ex.printStackTrace();
}
return "Server says: " + filename;
}
}
I've read somewhere on internet that you can't do file stream with RPC and I have to use Servlet for that. Is there any example of how to use Servlet and how to call that servlet from ReportsServiceImpl. Do I really need to make a servlet or it is possible to stream it back with my RPC?
You have to make a regular Servlet, you cannot stream binary data from ReportsServiceImpl. Also, there is no way to call the servlet from ReportsServiceImpl - your client code has to directly invoke the servlet.
On the client side, you'd have to create a normal anchor link with the parameters passed via the query string. Something like <a href="http://myserver.com/myservlet?parm1=value1&.."</a>.
On the server side, move your code to a standard Servlet, one that does NOT inherit from RemoteServiceServlet. Read the parameters from the request object, create the excel and send it back to the client. The browser will automatically popup the file download dialog box.
You can do that just using GWT RPC and Data URIs:
In your example, make your myMethod return the file content.
On the client side, format a Data URI with the file content received.
Use Window.open to open a file save dialog passing the formatted DataURI.
Take a look at this reference, to understand the Data URI usage:
Export to csv in jQuery
It's possible to get the binary data you want back through the RPC channel in a number of ways... uuencode, for instance. However, you would still have to get the browser to handle the file as a download.
And, based on your code, it appears that you are trying to trigger the standard browser mechanism for handling the given mime-type by modifying the response in the server so the browser will recognize it as a download... open a save dialog, for instance. To do that, you need to get the browser to make the request for you and you need the servlet there to handle the request. It can be done with rest urls, but ultimately you will need a serviet to do even that.
You need, in effect, to set a browser window URL to the URL that sends back the modified response object.
So this question (about streaming) is not really compatible with the code sample. One or the other (communication protocols or server-modified response object) approach has to be adjusted.
The easiest one to adjust is the communication method.

Categories