javaspringfilespring-mvc

Return file from Spring @Controller having OutputStream


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

  1. User enters url: /download/{file_name}

  2. 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
    }
    
  3. 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;
    }
    
  4. 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?


Solution

  • 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();
    }