복붙노트

[SPRING] OutputStream을 갖는 Spring @Controller에서 파일 반환

SPRING

OutputStream을 갖는 Spring @Controller에서 파일 반환

스프링 컨트롤러에서 파일을 반환하고 싶습니다. 이미 OutputStream 구현을 제공 할 수있는 API가 있으며 사용자에게 보내야합니다.

흐름은 다음과 같습니다.

출력 스트림 가져 오기-> 서비스는이 출력 스트림을 컨트롤러로 전달합니다.-> 컨트롤러가이를 사용자에게 보내야합니다

필자는 입력 스트림이 필요하다고 생각하며 다음과 같은 Apache Commons API 기능도 발견했습니다.

IOUtils.copy(InputStream is, OutputStream os)

그러나 문제는 그것을 다른면으로 변환하는 것입니다-> os에서 is로가 아니라 is에서 os로 변환됩니다.

대답이 옳지 않은 것을 보았 기 때문에 분명히하십시오. Dropbox API를 사용하고 OutputStream에서 파일을 받고 URL을 입력하는 동안이 출력 스트림을 사용자에게 보내려고합니다.

FileOutputStream outputStream = new FileOutputStream(); //can be any instance of OutputStream
DbxEntry.File downloadedFile = client.getFile("/fileName.mp3", null, outputStream);

그래서 출력 스트림을 입력 스트림으로 변환하는 방법에 대해 이야기했지만 방법을 모릅니다. 또한이 문제를 해결하는 더 좋은 방법이 있다고 가정합니다 (출력 스트림에서 어떻게 든 바이트 배열을 반환 할 수 있음)

dropbox에서 파일을 다운로드하는 메소드에 매개 변수를 통해 서블릿 출력 스트림 [response.getOutputstream ()]을 전달하려고 시도했지만 전혀 작동하지 않았습니다.

내 앱의 "흐름"은 다음과 같습니다. @Joeblade

그래서 dropboxClient.getFile () 메소드를 호출하여 다운로드 된 파일로 OutputStream을 수신하고 다운로드 된 파일을 포함하는이 OutputStream을 사용자에게 보내야합니다.이 작업을 수행하는 방법은 무엇입니까?

해결법

  1. ==============================

    1.ByteArrayOutputStream 및 ByteArrayInputStream을 사용할 수 있습니다. 예:

    ByteArrayOutputStream 및 ByteArrayInputStream을 사용할 수 있습니다. 예:

    // 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);
    

    다른 스트림 쌍과 동일한 작업을 수행 할 수 있습니다. 예 : 파일 스트림 :

    // 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");
    

    그리고 Spring MVC를 사용할 때 파일이 포함 된 byte []를 확실히 반환 할 수 있습니다. @ResponseBody로 응답에 주석을 달아야합니다. 이 같은:

    @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();
    } 
    
  2. ==============================

    2.HttpServletResponse에서 OutputStream을 가져 와서 파일을 작성하십시오 (이 예에서는 Apache Commons의 IOUtils 사용).

    HttpServletResponse에서 OutputStream을 가져 와서 파일을 작성하십시오 (이 예에서는 Apache Commons의 IOUtils 사용).

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

    예외가 발생하면 try / catch를 사용하여 스트림을 닫으십시오.

  3. ==============================

    3.가장 바람직한 솔루션은 InputStreamResource를 ResponseEntity와 함께 사용하는 것입니다. Content-Length를 수동으로 설정하기 만하면됩니다.

    가장 바람직한 솔루션은 InputStreamResource를 ResponseEntity와 함께 사용하는 것입니다. Content-Length를 수동으로 설정하기 만하면됩니다.

    @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);
    }
    
  4. ==============================

    4.이 답변을 읽는 것이 좋습니다

    이 답변을 읽는 것이 좋습니다

    @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);
    }
    

    의해 답변을 michal.kreuzman

    나는 비슷한 것을 직접 쓰려고했지만 물론 이미 대답했다.

    먼저 메모리에 모든 것을 가져 오는 대신 스트림을 전달하려면이 답변을 사용할 수 있습니다 나는 이것을 테스트하지 않았지만 (직장 아님) 합법적으로 보입니다. :)

    @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? 
    }
    

    문제는 IOUtils.copy / IOUtils.copyLarge와 거의 동일합니다. 라인 : 2128 당신이 말하는 것은 잘못된 방향을 복사합니다.

    그러나 먼저 요청한 내용을 이해해야합니다. 출력 스트림 (쓰기 용 객체)에서 읽고 입력 스트림 (읽기 용 객체)에 쓰려면 읽기 옵션을 제공하는 객체에 쓰는 것이 실제로라고 생각합니다.

    이를 위해 PipedInputStream 및 PipedOutputStream을 사용할 수 있습니다. 출력 스트림에 기록 된 바이트를 해당 입력 스트림에서 읽을 수 있도록 이들은 서로 연결됩니다.

    따라서 바이트를 수신하는 위치에서 바이트를 출력 스트림에 쓰고 있다고 가정합니다. 이 작업을 수행하십시오.

    // 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]); 
    

    IOUtils.copy를 사용하면 출력 스트림이 닫힐 때까지 계속 읽습니다. 따라서 시작하기 전에 (같은 스레드에서 쓰기 / 읽기를 실행하는 경우) 이미 닫혀 있는지 확인하거나 다른 스레드를 사용하여 출력 버퍼에 쓰고 끝에서 닫으십시오.

    그래도 이것이 원하는 내용이 아닌 경우 질문을 수정해야합니다.

  5. ==============================

    5.응답 출력 스트림에 쓸 때 명심해야 할 한 가지는 주기적으로 래핑 한 모든 라이터에서 flush ()를 호출하는 것이 좋습니다. 그 이유는 연결이 끊어진 경우 (예 : 사용자가 다운로드를 취소 한 경우) 오랫동안 예외를 발생시키지 않을 수 있기 때문입니다. 컨테이너에서 리소스가 효과적으로 유출 될 수 있습니다.

    응답 출력 스트림에 쓸 때 명심해야 할 한 가지는 주기적으로 래핑 한 모든 라이터에서 flush ()를 호출하는 것이 좋습니다. 그 이유는 연결이 끊어진 경우 (예 : 사용자가 다운로드를 취소 한 경우) 오랫동안 예외를 발생시키지 않을 수 있기 때문입니다. 컨테이너에서 리소스가 효과적으로 유출 될 수 있습니다.

  6. ==============================

    6.귀하의 경우 가장 메모리 효율적인 솔루션은 응답 OutputStream을 Dropbox API에 바로 전달하는 것입니다.

    귀하의 경우 가장 메모리 효율적인 솔루션은 응답 OutputStream을 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());
    }
    

    API가 읽은 데이터는 사용자에게 직접 전송됩니다. 모든 유형의 추가 바이트 버퍼가 필요하지 않습니다.

    PipedInputStream / PipedOutputStream은 두 스레드 간의 통신을 차단하기위한 것입니다. PipedOutputStream은 파이프의 끝 (PipedInputStream)에서 다른 스레드가 읽기를 시작할 때까지 1024 바이트 (기본적으로) 후에 스레드 쓰기를 차단합니다.

  7. from https://stackoverflow.com/questions/27741283/return-file-from-spring-controller-having-outputstream by cc-by-sa and MIT license