I've made a pretty conventional file upload method using dropwizard.
So I have a method in the resource that starts off like this
#POST
#Path("/uploadFile")
#Consumes(MediaType.MULTIPART_FORM_DATA)
#UnitOfWork
public Response uploadFile(
#FormDataParam("file") final InputStream fileInputStream,
#FormDataParam("file") final FormDataContentDisposition contentDispositionHeader,
#Context HttpServletRequest request) throws IOException {...
Nothing special inside, it just saves to a path, using java.nio libraries, like so
java.nio.file.Path outputPath = FileSystems.getDefault()
.getPath(System.getProperty("user.home"), fileName);
if (!Files.exists(outputPath.getParent()))
Files.createDirectories(outputPath.getParent());
Files.copy(fileInputStream, outputPath);
It just will not upload an excel file. I read elsewhere that excel files and their underlying type are viewed with suspicion. What do I need to do please?
Basically, the method worked for other files, but would not upload a xls file. I don't know the exact reason, but my suspicions are posted in the question. The way I got it working was to replace the simple copy line
Files.copy(fileInputStream, outputPath);
with
OutputStream outputStream = null;
try {
outputStream = new FileOutputStream(new File(outputPath.toString()));
int read = 0;
byte[] bytes = new byte[1024];
while ((read = fileInputStream.read(bytes)) != -1) {
outputStream.write(bytes, 0, read);
}
outputStream.flush();
}
catch (IOException ioe) {
ioe.printStackTrace();
}
finally{
outputStream.close();
}
Which gets around (what I suspect is a security feature) by reading the input stream into a byte array, and then writing it into a new FileOutputStream.
If anybody has a "proper way," that is better than this, it would be cool to see it. The stack trace didn't tell me much about what was going on.
Related
I have create Rest Service and I am trying to Generate Zip file. This Zip file created from muliple PDF files which are downloaded using method InputStream inpuStream = new URL(url).openStream() . I am able to Generate Zip file Which included PDF files but PDF files are broken.
Even If i try to Generate it from String its coming as broken PDF and i am getting Error message "Not a supported File Type or file is broken or damaged". Its simple code but seems like i am unable to track the mistake.
I have provided my controller , service method for your reference.
1)Controller:
#GetMapping("/getZipFile")
public void getZipFile(HttpServletResponse response) throws RestException {
try {
ByteArrayOutputStream baos = generateZipService.getZipFile();
ServletOutputStream responseOutPutStream = response.getOutputStream();
response.setContentType("APPLICATION/OCTET-STREAM");
response.setStatus(HttpServletResponse.SC_OK);
response.addHeader("Content-Disposition", "attachment; filename=\"GeneratedZipFile.zip\"");
responseOutPutStream.write(baos.toByteArray());
responseOutPutStream.flush();
} catch (Exception e) {
throw new RestException("Error In downloading Zip File");
}
}
2)Service Method
public ByteArrayOutputStream getZipFile() throws Exception{
List<ZipFileName> zipFileNames= zipFileNameDao.getZipFileName();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ZipOutputStream zipOut= new ZipOutputStream(baos);
for (String fileName : zipFileNames) {
InputStream inpuStream = new ByteArrayInputStream( "this is test to generarte pdf test file this is test tdfsfs this is test to generarte pdf test file this is test tdfsfs".getBytes(Charsets.UTF_8) );
createZipFile(inpuStream,zipOut,fileName);
inpuStream.close();
}
zipOut.flush();
baos.flush();
zipOut.close();
baos.close();
return baos;
}
3)createzipfile from service method :
```private void createZipFile(InputStream inputStream, ZipOutputStream zipOut,String fileName) throws IOException {
ZipEntry zipEntry = new ZipEntry(fileName+".pdf");
BufferedInputStream bis = new BufferedInputStream(inputStream);
zipOut.putNextEntry(zipEntry);
zipOut.write(IOUtils.toByteArray(inputStream));
zipOut.closeEntry();
bis.close();
inputStream.close();
}
Also , Another question is about using channels. I read channels are better when you have large files to downlaod from server . I have less then 20 kb of file so should I use Java.nio or just Zipoutputstream is fine.
I try with "response.setContentType("APPLICATION/ZIP")" but it didnt change the outcome of the project.
Thank you for your help..
The code worked fine only thing missing was to pass authentication with the openStream() method because of which I was getting the broken PDF. I opened the pdf with notepad++ and found the error ..
I resolved it.
Thank you
I have made a rest api in this it is working fine but i want to read size of file and i have used below code to read size of file
#POST
#Path("/test")
#Consumes(MediaType.MULTIPART_FORM_DATA)
public Response upload( FormDataMultiPart form ){
System.out.println(" size of file="+ filePart.getContentDisposition().getSize());
}
but i got the size of file -1 .
Can any one suggest me how can i read actual size of file .
But using
System.out.println(" data name ="+ filePart.getContentDisposition().getFileName());
i got correct name of file .
First of all, I think the best way to restrict data size is setting on the server configuration. Please see the related ref.
Secondly, since it comes with the Stream, it goes til to meet EOF or similar one. That means you cannot find the size at the first place.
Third, however, one of the alternative way for judging size in jersey is using the contents size of the HTTP header. The server gets 'Content-Length' header at the first place to know the size of the body. #Context HttpServletRequest request has request.getContentLength() method. But since it has multiparts, you need to be careful that the body size has sum of the data with protocol overheads (seperator/contents infos)
Good Luck
Hope this is what you wanted. I have verified it in my system. This prints the size of the file in bytes.
#POST
#Path("/upload")
#Consumes(MediaType.MULTIPART_FORM_DATA)
public Response uploadFile(#FormDataParam("file") InputStream uploadedInputStream, #FormDataParam("file") FormDataContentDisposition fileDetail) {
String uploadedFileLocation = "/home/Desktop/" + fileDetail.getFileName();
// save it
writeToFile(uploadedInputStream, uploadedFileLocation);
File file = new File(uploadedFileLocation);
System.out.println(file.length() + " in bytes");
String output = "File uploaded to : " + uploadedFileLocation;
return Response.status(200).entity(output).build();
}
// save uploaded file to new location
private void writeToFile(InputStream uploadedInputStream, String uploadedFileLocation) {
try {
OutputStream out = new FileOutputStream(new File(uploadedFileLocation));
int read = 0;
byte[] bytes = new byte[1024];
out = new FileOutputStream(new File(uploadedFileLocation));
while ((read = uploadedInputStream.read(bytes)) != -1) {
out.write(bytes, 0, read);
}
out.flush();
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
make sure you have the following dependency in your pom.xml
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-multipart</artifactId>
<version>2.13</version>
</dependency>
also add this to your application class which extends resource config. This registers your class with jersey as having multipart content.
super(YourClass.class, MultiPartFeature.class);
Try using HttpServletRequest . Kindly see the docs for more details.
#Consumes(MediaType.MULTIPART_FORM_DATA)
public Response putFile(#Context HttpServletRequest request){
Part part = request.getPart("filename");
long fileSize = part.getSize();
}
I want to return a file from a Spring controller. I already have API that can give me any implementation of OutputStream and then I need to send it to a user.
So the flow is something like that:
getting outputstream -> service passes this outputstream to controller -> controller has to send it to a user
I think I need inputstream to do it and I have also found Apache Commons api feature that looks like this:
IOUtils.copy(InputStream is, OutputStream os)
but the problem is, it converts it to the other side -> not from os to is, but from is to os.
Edit
to be clear, because I see the answers are not hitting right thing:
I use Dropbox api and recieve file in OutputStream and I want this output stream to be sent to user while entering some URL
FileOutputStream outputStream = new FileOutputStream(); //can be any instance of OutputStream
DbxEntry.File downloadedFile = client.getFile("/fileName.mp3", null, outputStream);
Thats why i was talking about converting outputstream to inputstream, but have no idea how to do it. Furthermore, I suppose that there is better way to solve this (maybe return byte array somehow from outputstream)
I was trying to pass servlet outputstream [response.getOutputstream()] through parameter to the method that downloads file from dropbox, but it didnt work, at all
Edit 2
The "flow" of my app is something like this: #Joeblade
User enters url: /download/{file_name}
Spring Controller captures the url and calls the #Service layer to download the file and pass it to that controller:
#RequestMapping(value = "download/{name}", method = RequestMethod.GET)
public void getFileByName(#PathVariable("name") final String name, HttpServletResponse response) throws IOException {
response.setContentType("audio/mpeg3");
response.setHeader("Content-Disposition", "attachment; filename=" + name);
service.callSomeMethodAndRecieveDownloadedFileInSomeForm(name); // <- and this file(InputStream/OutputStream/byte[] array/File object/MultipartFile I dont really know..) has to be sent to the user
}
Now the #Service calls Dropbox API and downloads the file by specified file_name, and puts it all to the OutputStream, and then passes it (in some form.. maybe OutputStream, byte[] array or any other object - I dont know which is better to use) to the controller:
public SomeObjectThatContainsFileForExamplePipedInputStream callSomeMethodAndRecieveDownloadedFileInSomeForm(final String name) throws IOException {
//here any instance of OutputStream - it needs to be passed to client.getFile lower (for now it is PipedOutputStream)
PipedInputStream inputStream = new PipedInputStream(); // for now
PipedOutputStream outputStream = new PipedOutputStream(inputStream);
//some dropbox client object
DbxClient client = new DbxClient();
try {
//important part - Dropbox API downloads the file from Dropbox servers to the outputstream object passed as the third parameter
client.getFile("/" + name, null, outputStream);
} catch (DbxException e){
e.printStackTrace();
} finally {
outputStream.close();
}
return inputStream;
}
Controler recieves the file (I dont know, at all, in which form as I said upper) and passes it then to the user
So the thing is to recieve OutputStream with the downloaded file by calling dropboxClient.getFile() method and then this OutputStream that contains the downloaded file, has to be sent to the user, how to do this?
Get the OutputStream from the HttpServletResponse and write the file to it (in this example using IOUtils from Apache Commons)
#RequestMapping(value = "/download", method = RequestMethod.GET)
public void download(HttpServletResponse response) {
...
InputStream inputStream = new FileInputStream(new File(PATH_TO_FILE)); //load the file
IOUtils.copy(inputStream, response.getOutputStream());
response.flushBuffer();
...
}
Make sure you use a try/catch to close the streams in case of an exception.
The most preferable solution is to use InputStreamResource with ResponseEntity. All you need is set Content-Length manually:
#RequestMapping(value = "/download", method = RequestMethod.GET)
public ResponseEntity download() throws IOException {
String filePath = "PATH_HERE";
InputStream inputStream = new FileInputStream(new File(filePath));
InputStreamResource inputStreamResource = new InputStreamResource(inputStream);
HttpHeaders headers = new HttpHeaders();
headers.setContentLength(Files.size(Paths.get(filePath)));
return new ResponseEntity(inputStreamResource, headers, HttpStatus.OK);
}
You could use the ByteArrayOutputStream and ByteArrayInputStream. Example:
// A ByteArrayOutputStream holds the content in memory
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
// Do stuff with your OutputStream
// To convert it to a byte[] - simply use
final byte[] bytes = outputStream.toByteArray();
// To convert bytes to an InputStream, use a ByteArrayInputStream
ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes);
You can do the same with other stream pairs. E.g. the file streams:
// Create a FileOutputStream
FileOutputStream fos = new FileOutputStream("filename.txt");
// Write contents to file
// Always close the stream, preferably in a try-with-resources block
fos.close();
// The, convert the file contents to an input stream
final InputStream fileInputStream = new FileInputStream("filename.txt");
And, when using Spring MVC you can definitely return a byte[] that contains your file. Just make sure that you annotate your response with #ResponseBody. Something like this:
#ResponseBody
#RequestMapping("/myurl/{filename:.*}")
public byte[] serveFile(#PathVariable("file"} String file) throws IOException {
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
DbxEntry.File downloadedFile = client.getFile("/" + filename, null, outputStream);
return outputStream.toByteArray();
}
I recommend reading this answer
#ResponseBody
#RequestMapping("/photo2", method = RequestMethod.GET, produces = MediaType.IMAGE_JPEG_VALUE)
public byte[] testphoto() throws IOException {
InputStream in = servletContext.getResourceAsStream("/images/no_image.jpg");
return IOUtils.toByteArray(in);
}
answered by michal.kreuzman
I was going to write something similar myself but ofcourse it's already been answered.
If you want to just pass the stream instead of first getting everything in memory you could use this answer
I haven't tested this (not at work) but it looks legit :)
#RequestMapping(value = "report1", method = RequestMethod.GET, produces = "application/pdf")
#ResponseBody
public void getReport1(OutputStream out) {
InputStream in; // retrieve this from wherever you are receiving your stream
byte[] buffer = new byte[1024];
int len;
while ((len = in.read(buffer)) != -1) {
out.write(buffer, 0, len);
}
in.close();
out.flush(); // out.close?
}
The thing is, this is pretty much the same as IOUtils.copy / IOUtils.copyLarge does. line: 2128
Which you say copies the wrong direction.
However first make sure you understand what you ask. If you want to read from an outputstream(object for writing) and write to an input stream (object to read from) then I think what you really want is to write to an object that also supplies a read option.
for that you could use a PipedInputStream and PipedOutputStream. These are connected together so that bytes written to the outputstream are available to be read from the corresponding input stream.
so in the location where you are receiving the bytes I assume you are writing bytes to an outputstream.
there do this:
// set up the input/output stream so that bytes written to writeToHere are available to be read from readFromhere
PipedInputStream readFromHere = new PipedInputStream();
PipedOutputStream writeToHere = new PipedOutputStream(readFromHere);
// write to the outputstream as you like
writeToHere.write(...)
// or pass it as an outputstream to an external method
someMather(writeToHere);
// when you're done close this end.
writeToHere.close();
// then whenever you like, read from the inputstream
IOUtils.copy(readFromHere, out, new byte[1024]);
If you use IOUtils.copy it will continue to read until the outputstream is closed. so make sure that it is already closed before starting (if you run write/read on the same thread) or use another thread to write to the output buffer and close it at the end.
If this is still not what you're looking for then you'll have to refine your question.
The most memory-efficient solution in your case would be to pass the response OutputStream right to the Dropbox API:
#GetMapping(value = "download/{name}")
public void getFileByName(#PathVariable("name") final String name, HttpServletResponse response)
throws IOException, DbxException {
response.setContentType("audio/mpeg3");
response.setHeader(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + name + "\"");
response.setContentLength(filesize); // if you know size of the file in advance
new DbxClient().getFile("/" + name, null, response.getOutputStream());
}
Data read by the API will be sent directly to the user. No additional byte buffer of any type is required.
As for PipedInputStream/PipedOutputStream, they are intended for the blocking communication between 2 threads. PipedOutputStream blocks writing thread after 1024 bytes (by default) until some other thread start reading from the end of the pipe (PipedInputStream).
One thing to keep in mind when writing to the response outputstream is that it is a very good idea to call flush() on whatever writer that you've wrapped it with periodically. The reason for this is that a broken connection (for example caused by a user canceling a download) may not end up throwing an exception for a long time, if ever. This can effectively be a resource leak on your container.
I am trying to understand what is the correct way to read a file in Java Servlet program. I need to read a file from a fixed path on my machine using my servlet code. Now I can read the file in multiple ways and one of the way which I am planning to use is to read the information in bytes as shown in below code:
private static void readFile(HttpServletRequest req, HttpServletResponse resp, String path)
throws IOException
{
File file = new File("C:\\temp\", path);
if (!file.isFile()) {
resp.sendError(404, "File not found: " + file);
return;
}
InputStream in = null;
ServletOutputStream out = null;
try {
resp.setContentLength(Long.valueOf(file.length()).intValue());
resp.resetBuffer();
out = resp.getOutputStream();
in = new BufferedInputStream(new FileInputStream(file));
readFile(in, out);
}
finally {
//Code for closing the input & output steams
}
}
}
public static void readFile(InputStream in, OutputStream out) throws IOException
{
byte[] buf = new byte[4096];
int data;
while ((data = in.read(buf, 0, buf.length)) != -1)
out.write(buf, 0, data);
}
I don't have issues with this logic and it is working fine.
Now I came across the post How To Read File In Java – BufferedReader in mykyong site and here the example uses BufferedReader.
Can someone please tell me which is the efficient way of reading a file in servlet code? when we need to prefer using BufferedReader in comparison to reading data in bytes.
There's almost no reason to read a file manually anymore since Java 7's NIO.
Just use Files.readAllBytes(Path) to read the full byte[]. Or if you want to stream directly to an OutputStream, Files.copy(Path, OutputStream).
Can someone please tell me which is the efficient way of reading a
file in servlet code? when we need to prefer using BufferedReader in
comparison to reading data in bytes.
Any buffered method will work. Here, BufferedReader allows you to read streams as String values. As the javadoc says
Reads text from a character-input stream, buffering characters so as
to provide for the efficient reading of characters, arrays, and lines.
My goal is to put multiple java.io.File objects into a zip file and print to HttpServletResponse for the user to download.
The files were created by the JAXB marshaller. It's a java.io.File object, but it's not actually on the file system (it's only in memory), so I can't create a FileInputStream.
All resources I've seen use the OutputStream to print zip file contents. But, all those resources use FileInputStream (which I can't use).
Anyone know how I can accomplish this?
Have a look at the Apache Commons Compress library, it provides the functionality you need.
Of course "erickson" is right with his comment to your question. You will need the file content and not the java.io.File object. In my example I assume that you have a method
byte[] getTheContentFormSomewhere(int fileNummer) which returns the file content (in memory) for the fileNummer-th file. -- Of course this function is poor design, but it is only for illustration.
It should work a bit like this:
void compress(final OutputStream out) {
ZipOutputStream zipOutputStream = new ZipOutputStream(out);
zipOutputStream.setLevel(ZipOutputStream.STORED);
for(int i = 0; i < 10; i++) {
//of course you need the file content of the i-th file
byte[] oneFileContent = getTheContentFormSomewhere(i);
addOneFileToZipArchive(zipOutputStream, "file"+i+"."txt", oneFileContent);
}
zipOutputStream.close();
}
void addOneFileToZipArchive(final ZipOutputStream zipStream,
String fileName,
byte[] content) {
ZipArchiveEntry zipEntry = new ZipArchiveEntry(fileName);
zipStream.putNextEntry(zipEntry);
zipStream.write(pdfBytes);
zipStream.closeEntry();
}
Snipets of your http controller:
HttpServletResponse response
...
response.setContentType("application/zip");
response.addHeader("Content-Disposition", "attachment; filename=\"compress.zip\"");
response.addHeader("Content-Transfer-Encoding", "binary");
ByteArrayOutputStream outputBuffer = new ByteArrayOutputStream();
compress(outputBuffer);
response.getOutputStream().write(outputBuffer.toByteArray());
response.getOutputStream().flush();
outputBuffer.close();
Turns out I'm an idiot :) The file that was being "created" was saving to invalid path and swallowing the exception, so I thought it was being "created" ok. When I tried to to instantiate a new FileInputStream, however, it complained that file didn't exist (rightly so). I had a brainfart and assumed that the java.io.File object actually contained file information in it somewhere. But as erickson pointed out, that was false.
Thanks Ralph for the code, I used it after I solved the invalid pathing issue.
My code:
ZipOutputStream out = new ZipOutputStream(response.getOutputStream());
byte[] buf = new byte[1024];
File file;
InputStream in;
// Loop through entities
for (TitleProductAccountApproval tpAccountApproval : tpAccountApprovals) {
// Generate the file
file = xmlManager.getXML(
tpAccountApproval.getTitleProduct().getTitleProductId(),
tpAccountApproval.getAccount().getAccountId(),
username);
// Write to zip file
in = new FileInputStream(file);
out.putNextEntry(new ZipEntry(file.getName()));
int len;
while ((len = in.read(buf)) > 0) {
out.write(buf, 0, len);
}
out.closeEntry();
in.close();
}
out.close();