When the file size is less than 8k, the file name display correctly,
When more than 8k, can not display, after modify the file suffix, open the file, the data is output completely.How to do
response.reset();
OutputStream fileoutStream = response.getOutputStream();
hssfWorkbook.write(fileoutStream);
String filename = new String(wbname.getBytes(), "ISO-8859-1");
resp.setContentType("application/vnd.ms-excel,charset=utf-8");
resp.addHeader("Content-Disposition", "attachment;filename=\""+filename+".xls\"");
enter image description here
You need to add the headers before writing to the OutputStream. It works when the file is small enough because the output is buffered, but technically I think that's a bug. The HTTP protocol requires that all the headers are sent to the client first, and then the payload. You can't go back and add headers once the first byte of the payload is flushed to the socket.
Related
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.
I have a jsp page that is used to download pdf file from server. When I request the jsp page from browser, the jsp read file info from database, get inputstream from file and write to ServletOutputStream.
The pseudo-code like this:
response.setContentType("application/pdf");
response.setHeader("Content-disposition","inline;filename=URLEncoder.encode(filename)");
response.setHeader("Cache-Control","max-age=3600");
ServletOutputStream os = response.getOutputStream();
FileInputStream in = new FileInputStream(new File(filePath));
int size = 0;
byte[] buffer = new byte[512];
while((size=in.read(buffer))!=-1){
os.write(buffer,0,size);
}
when the request finished, the downloaded pdf was opened in browser. The same file is downloaded many times, so we want to cache them to the local file system, so that each request's return status code is 304(not modified), but the cache-control doesn't work, is there anybody can help?
This problem is typically solved by a reverse proxy like NGINX or Varnish in front of your application. Alternatively you can use a CDN.
I have developed a servlet that offers some services.
I am using apache-commons-net FTPClient to log into a ftp server and read a file.
I want to make this file downloadle (aka send it to the outputstream maybe?) , but the only ways of reading a file that i know of are:
FTPClient.retrieveFileStream(String remote) and FTPClient.retrieveFile(String remote, OutputStream local).
I tried the first one and then wrote the InputStream i got to the outputStream of the servlet:
InputStream myFileStream = FTPClient.retrieveFileStream(fileName);
byte[] buffer = new byte[4096];
int length;
resp.reset();
resp.setContentType("text/csv");
resp.setHeader("Content-disposition","attachment; filename=\""+fileName+"\"");
OutputStream out = resp.getOutputStream();
while((length=myFileStream.read(buffer)) > 0){
out.write(buffer, 0, length);
}
myFileStream.close();
out.flush();
The Second One:
myClient.retrieveFile(fileName, resp.getOutputStream());
In both cases i get the text content of the file as a response and not the file itself.
Is there any way i can do this.
P.s. this code belongs to a medhod that is being called by the doPost() with http req and http resp as parameters.
If you want to download the file instead of just showing it, you have to change the content type you're sending to the browser (because it's browser's business to either display the data or save them as a file). Thus, do e.g.
resp.setContentType("application/octet-stream");
(instead of text/csv) to "hide" the real nature of the data from the browser and force it to save the data.
The problem was that i was using a google extension (DHC) to test my web service. and it displayed the file content instead of initializing the download.
I was making the file download in a doPost() method.
Solution:
I made it in a doGet() method and when accessed directly via browser everything works ok.
So i think it was only the extensions problem, which wrote the content of the response back to me instead of downloading the file attachment.
Thanks for the feedback to #Jozef
Everything works fine, but only if file is small, about 1MB, when I tried it with bigger files, like 20MB my browser display it, instead of force to download, I tried many headers so far, now my code looks:
PrintWriter out = response.getWriter();
String fileName = request.getParameter("filename");
File f= new File(fileName);
InputStream in = new FileInputStream(f);
BufferedInputStream bin = new BufferedInputStream(in);
DataInputStream din = new DataInputStream(bin);
while(din.available() > 0){
out.print(din.readLine());
out.print("\n");
}
response.setContentType("application/force-download");
response.setContentLength((int)f.length());
response.setHeader("Content-Transfer-Encoding", "binary");
response.setHeader("Content-Disposition","attachment; filename=\"" + "xxx\"");//fileName);
in.close();
bin.close();
din.close();
You are setting the response headers after writing the contents of the file to the output stream. This is quite late in the response lifecycle to be setting headers. The correct sequence of operations should be to set the headers first, and then write the contents of the file to the servlet's outputstream.
Therefore, your method should be written as follows (this won't compile as it is a mere representation):
response.setContentType("application/force-download");
response.setContentLength((int)f.length());
//response.setContentLength(-1);
response.setHeader("Content-Transfer-Encoding", "binary");
response.setHeader("Content-Disposition","attachment; filename=\"" + "xxx\"");//fileName);
...
...
File f= new File(fileName);
InputStream in = new FileInputStream(f);
BufferedInputStream bin = new BufferedInputStream(in);
DataInputStream din = new DataInputStream(bin);
while(din.available() > 0){
out.print(din.readLine());
out.print("\n");
}
The reason for the failure is that it is possible for the actual headers sent by the servlet would be different from what you are intending to send. After all, if the servlet container does not know what headers (which appear before the body in the HTTP response), then it may set appropriate headers to ensure that the response is valid; setting the headers after the file has been written is therefore futile and redundant as the container might have already set the headers. You could confirm this by looking at the network traffic using Wireshark or a HTTP debugging proxy like Fiddler or WebScarab.
You may also refer to the Java EE API documentation for ServletResponse.setContentType to understand this behavior:
Sets the content type of the response being sent to the client, if the response has not been committed yet. The given content type may include a character encoding specification, for example, text/html;charset=UTF-8. The response's character encoding is only set from the given content type if this method is called before getWriter is called.
This method may be called repeatedly to change content type and character encoding. This method has no effect if called after the response has been committed.
...
Set content-type and other headers before you write the file out. For small files the content is buffered, and the browser gets the headers first. For big ones the data come first.
This is from a php script which solves the problem perfectly with every browser I've tested (FF since 3.5, IE8+, Chrome)
header("Content-Disposition: attachment; filename=\"".$fname_local."\"");
header("Content-Type: application/force-download");
header("Content-Transfer-Encoding: binary");
header("Content-Length: ".filesize($fname));
So as far as I can see, you're doing everything correctly. Have you checked your browser settings?
Is there a way to determine the size of the HTTPServletResponse content? I read this get-size-of-http-response-in-java question but sadly where I work I do not have access to CommonsIO :(
The response content consists of a single complex object so I have considered writing it out to a temp file and then checking that file. This is not something I want to be doing as a diagnostic while the application is running in production though so want to avoid it if at all possible.
PS I read erickson's answer but it mentioned input streams I want to know the size of the object being written out... Would be really nice if the writeObject() method returned a number representing bytes written instead of void...
If you have access to the response header, you can read the Content-Length.
Here is a example of a response header:
(Status-Line):HTTP/1.1 200 OK
Connection:Keep-Alive
Date:Fri, 25 Mar 2011 16:26:56 GMT
Content-Length:728
Check this out: Header Field Definitions
This seems to be what you're looking for:
DataOutputStream dos = new DataOutputStream(response.getOutputStream());
...
int len = dos.size();
I eventually found a way to get what I wanted:
URLConnection con = servletURL.openConnection();
BufferedInputStream bif = new BufferedInputStream(con.getInputStream());
ObjectInputStream input = new ObjectInputStream(bif);
int avail = bif.available();
System.out.println("Response content size = " + avail);
This allowed me to see the response size on the client. I still would like to know what it is on the server side before it is sent but this was the next best thing.
Assuming the use of ObjectOutputStream, build it around a java.io.ByteArrayOutputStream:
ByteArrayOutputStream contentBytes = new ByteArrayOutputStream();
ObjectOutputStream objectOut = new ObjectOutputStream(contentBytes);
objectOut.writeObject(content);
int contentLength = contentBytes.size();
And then you can send the content with
contentBytes.writeTo(connection.getOutputStream());
where connection is whatever you're getting your OutputStream from.
Better late than never, right?