This question already has answers here:
Easy way to write contents of a Java InputStream to an OutputStream
(24 answers)
Closed 6 years ago.
So this is the code I have, which already works:
public class MyServlet extends HttpServlet {
#Override
protected void doGet(HttpServletRequest req, HttpServletResponse response) throws Exception {
String pathToFile = "myimage.jpg";
File file = new File(pathToFile);
response.setHeader("Content-Type", "image/jpeg");
response.setHeader("Content-Length", String.valueOf(file.length()));
response.setHeader("Content-Disposition", "inline; filename=\"" + file.getName() + "\"");
Files.copy(file.toPath(), response.getOutputStream());
response.flushBuffer();
}
}
However, I must make this work with JDK 1.6.
Files.copy is only available with Java 1.7.
Any suggestions?
You can use Apache commons IOutils.
IOUtils.copy(InputStream input, OutputStream output)
Java 6 didn’t comes with any ready make file copy function, you have to manual create the file copy process. To copy file, just convert the file into a bytes stream with FileInputStream and write the bytes into another file with FileOutputStream.
As there's no way to do this a lot easier with JDK methods.You could use IOUtils from Apache Commons IO, it also has a lot of other useful things.
IOUtils.copy(inputStream, outputStream);
Or else using Guava's ByteStreams.copy() you can achieve the same functionality.
ByteStreams.copy(inputStream, outputStream);
Related
I am trying to write a spring boot java code to compress a Directory using REST API call. I find many codes using plain Java, but I am unable to find a one in Spring boot Java. I tried the below code and its zipping the file but when i try to zip the directory it throws FilenotFound Exception. Could anyone please help me with a working code. I dont know where I am going wrong. I am a beginner to Spring boot.
#RequestMapping(value="/zip", produces="application/zip")
public void zipFiles(HttpServletResponse response) throws IOException {
response.setStatus(HttpServletResponse.SC_OK);
response.addHeader("Content-Disposition", "attachment; filename=\"compressedfile.zip\"");
ZipOutputStream zipOutputStream = new ZipOutputStream(response.getOutputStream());
ArrayList<File> files = new ArrayList<>();
files.add(new File("C:\\Pavan\\zipfolder")); //(zip is working if(C:\\Pavan\\zipfolder\\dockerfile.txt)
for (File file : files) {
zipOutputStream.putNextEntry(new ZipEntry(file.getName()));
FileInputStream fileInputStream = new FileInputStream(file);
IOUtils.copy(fileInputStream, zipOutputStream);
fileInputStream.close();
zipOutputStream.closeEntry();
}
zipOutputStream.close();
}
Error:
You are passing a directory to the FileInputStream constructor. This is not allowed, see Javadoc. Instead you have to iterate over the files contained in this directory. See Zipping and Unzipping in Java for an example.
I am using this code to download an existing file from the server on Liferay (6.2) into a local pc:
`
File file = getFile(diskImage.getImageType(), diskImage.getId());
HttpServletRequest httpReq = PortalUtil.getHttpServletRequest(request);
HttpServletResponse httpResp = PortalUtil.getHttpServletResponse(response);
httpResp.setContentType("application/octet-stream");
httpResp.setHeader("Content-Transfer-Encoding", "binary");
httpResp.setHeader("Content-Length", String.valueOf(file.length()));
httpResp.setHeader("Content-Disposition", "attachment; filename=" + file.getName());
try (InputStream input = new FileInputStream(file)) {
ServletResponseUtil.sendFile(httpReq, httpResp, file.getName(), input, "application/octet-stream");
} catch (Exception e) {
throw new FilesManagerException(e);
}
}
`
This code works fine only for small files. But downloading large files (cca 2GB) throws javax.portlet.PortletException: Error occurred during request processing: Java heap space.
How to fix this code so it works properly for larger files as well?
I guess that the suitable approach would be to use some kind of a buffer for large files and I try it but it wouldn't work even for the smaller files afterwards.
First of all: I'm assuming you're doing this in a render method - and this is just plain wrong. Sooner or later this will break, because you don't have control over the output stream: It might already be committed and transmit data to the browser when your portlet starts to render. In render you always must generate a portlet's HTML code.
Instead, you should go to the resource serving phase of a portlet. With the ResourceRequest and ResourceResponse, you have a very similar support for setting mimetypes as with HttpServletResponse.
And for exactly that reason, ServletResponseUtil is indeed the wrong place to look for. If you use anything from Liferay, you should look for PortletResponseUtil. There are various sendFile methods that accept a byte[], others accept a stream or a file. I'd recommend to try these, if they still fail, look at the implementation you are ending up with. In the worst case, just don't use any of the Util methods. Copying content from one stream to another is not too bad. (Actually, you give no clue about the static type of your variable input in the question: If that's a byte[], there's your solution)
You might want to file an issue with Liferay, if indeed the pure stream-transfer does read the whole file into memory, but your quickfix (in case this is indeed a bug) would be to copy the data yourself.
Thanks for thoughts, finally I used PortletResponseUtil.sendFile(...); method and changed actionURL to responseURL in .jsp file. So that I implemented serveResource()with above mentioned method. It seems that everything is working fine.
ServletResponseUtil.sendFile(httpReq, httpResp, file.getName(), input, "application/octet-stream"); what's this?
Don't read a file once time.Use a buffer.
response.reset();
response.setContentType("application/x-download");
response.addHeader("Content-Disposition","attachment;filename="+new String(filename.getBytes(),"utf-8"));
response.addHeader("Content-Length",""+file.length());
OutputStream toClient=new BufferedOutputStream(response.getOutputStream());
response.setContentType("application/octet-stream");
byte[] buffer=new byte[1024*1024*4];
int i=-1;
while((i=fis.read(buffer))!=-1){
toClient.write(buffer,0,i);
}
fis.close();
toClient.flush();
toClient.close();
We have the below requirement.
We will have to create an excel/pdf report and then download it on click of a button in a java web application. The pdf/excel file is dynamically created using application data.
We should not create any physical file on the server.
How do we go about this? Are there any streams through which I can read and write in the same go without having to close in between.
You could use memory-based streams (such as ByteArrayInputStream and ByteArrayOutputStream) and use the same underlying byte buffer to address the read/write in the same go part of the question.
As others have pointed out, you can just write directly to the output stream of the response.
Look at ServletResponse.getOutputStream().
You need to write to this stream from the one created by your report API. Don't forget to set the proper content-type using setContentType() method of the same class.
Here you can find how you can do it with jxl API and it may help you also.
How do I output an Excel file from a Servlet?
Whatever PDF or Excel API you are using to generate the files, you should lookup the constructor or method which takes an OutputStream to write the generated PDF/Excel content to. You should just feed it with response.getOutputStream() instead of FileOutputStream.
For example, iText for PDFs:
response.setContentType("application/pdf");
response.setHeader("Content-Disposition", "attachment; filename=\"" + filename + "\"");
PdfWriter pdfWriter = PdfWriter.getInstance(document, response.getOutputStream());
// ...
And Apache POI for Excel:
response.setContentType("application/vnd.ms-excel");
response.setHeader("Content-Disposition", "attachment; filename=\"" + filename + "\"");
WritableWorkbook workBook = Workbook.createWorkbook(response.getOutputStream());
// ...
Have a Servlet serve the pdf/excel file as a byte array.
#Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
byte[] bytes = null; // get this from somewhere in your app
String fileName = "filename.pdf"; // whatever you wish to name the file
ServletOutputStream out = response.getOutputStream();
response.setHeader("Content-Disposition", "attachment; filename=\"" + fileName + "\"");
response.setContentType("application/pdf");
response.setContentLength(bytes.length);
out.write(bytes);
out.flush();
}
MIME type for MS Excel files would be application/vnd.ms-excel.
When I want to write the full contents of a file into an OutputStream, I usually allocate a buffer as a byte[], then make a for loop to read data from the file's InputStream into the buffer and write the buffer contents into the OutputStream, until the InputStream has no more bytes available.
This seems rather clumsy to me. Is there a better way to do this?
Also, I am always unsure about the buffer size. Usually, I am allocating 1024 bytes, because it just feels good. Is there a better way to determine a reasonable buffer size?
In my current case, I want to copy the full contents of a file into the output stream that writes the contents of an HTTP response. So, this is not a question about how to copy files on the file system.
For Java 1.7+ you can use the Files.copy(Path, OutputStream), e.g.
HttpServletResponse response = // ...
File toBeCopied = // ...
try (OutputStream out = response.getOutputStream()) {
Path path = toBeCopied.toPath();
Files.copy(path, out);
out.flush();
} catch (IOException e) {
// handle exception
}
Note, since you are dealing with HttpServletResponse is is also a good idea to set correct response headers. Add the following lines before you copy the actual file data to the response:
String mimeType = URLConnection.guessContentTypeFromName(toBeCopied.getName());
String contentDisposition = String.format("attachment; filename=%s", toBeCopied.getName());
int fileSize = Long.valueOf(toBeCopied.length()).intValue();
response.setContentType(mimeType);
response.setHeader("Content-Disposition", contentDisposition);
response.setContentLength(fileSize);
Note, the encoding of the file name passed to the content disposition is important, see this question.
Apache Commons-IO:
IOUtils.copy(fileInputStream,outputStream);
JDK NIO
new FileInputStream(file).getChannel().transferTo(otherChannel);
With commons-io you have a one-line solution:
IOUtils.copy(yourFileInputStream, outputStream);
Note that you'd have to close your streams manually (or by IOUtils.closeQuitely(..))
I know this is a little broad, but here's the situation:
I am using JSP and Java. I have a file located on my server. I would like to add a link to the screen that, when clicked, would open the file for the user to view. The file can either appear in a window in the web browser, or pop up the program needed to open the file (similar to when you are outputting with iText to the screen, where Adobe opens to display the file). I know my output stream already, but how can I write the file to the output stream? Most of what I have read has only dealt with text files, but I might be dealing with image files, etc., as well.
Any help is appreciated!
Thanks!
You need to add certain fields to the response. For a text/csv, you'd do:
response.setContentType("text/csv"); // set MIME type
response.setHeader("Content-Disposition", "attachment; filename=\"" strExportFileName "\"");
Here's a forum on sun about it.
Here's a simple implementation on how to achieve it:
protected void doPost(final HttpServletRequest request,
final HttpServletResponse response) throws ServletException,
IOException {
// extract filename from request
// TODO use a whitelist to avoid [path-traversing][1]
File file = new File(getFileName(request));
InputStream input = new FileInputStream(file);
response.setContentLength((int) file.length());
// TODO map your file to the appropriate MIME
response.setContentType(getMimeType(file));
OutputStream output = response.getOutputStream();
byte[] bytes = new byte[BUFFER_LENGTH];
int read = 0;
while (read != -1) {
read = input.read(bytes, 0, BUFFER_LENGTH);
if (read != -1) {
output.write(bytes, 0, read);
output.flush();
}
}
input.close();
output.close();
}
You need to create a 'download' servlet which writes the file to the response output stream with correct mime types. You can not reliably do this from within a .jsp file.
We usually do it with a 'download servlet' which we set the servletmapping to /downloads, then append path info to identify the asset to serve. The servlet verifies the request is valid, sets the mime header then delivers the file to the output stream. It's straightforward, but keep the J2EE javadocs handy while doing it.