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?
The most memory-efficient solution in your case would be to pass the response
OutputStream
right to the Dropbox API: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.
You could use the
ByteArrayOutputStream
andByteArrayInputStream
. Example:You can do the same with other stream pairs. E.g. the file streams:
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:The most preferable solution is to use InputStreamResource with
ResponseEntity
. All you need is setContent-Length
manually:Get the OutputStream from the HttpServletResponse and write the file to it (in this example using IOUtils from Apache Commons)
Make sure you use a try/catch to close the streams in case of an exception.
I recommend reading this answer
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 :)
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:
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.