[SPRING] OutputStream을 갖는 Spring @Controller에서 파일 반환
SPRINGOutputStream을 갖는 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.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.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.가장 바람직한 솔루션은 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.이 답변을 읽는 것이 좋습니다
이 답변을 읽는 것이 좋습니다
@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.응답 출력 스트림에 쓸 때 명심해야 할 한 가지는 주기적으로 래핑 한 모든 라이터에서 flush ()를 호출하는 것이 좋습니다. 그 이유는 연결이 끊어진 경우 (예 : 사용자가 다운로드를 취소 한 경우) 오랫동안 예외를 발생시키지 않을 수 있기 때문입니다. 컨테이너에서 리소스가 효과적으로 유출 될 수 있습니다.
응답 출력 스트림에 쓸 때 명심해야 할 한 가지는 주기적으로 래핑 한 모든 라이터에서 flush ()를 호출하는 것이 좋습니다. 그 이유는 연결이 끊어진 경우 (예 : 사용자가 다운로드를 취소 한 경우) 오랫동안 예외를 발생시키지 않을 수 있기 때문입니다. 컨테이너에서 리소스가 효과적으로 유출 될 수 있습니다.
-
==============================
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 바이트 (기본적으로) 후에 스레드 쓰기를 차단합니다.
from https://stackoverflow.com/questions/27741283/return-file-from-spring-controller-having-outputstream by cc-by-sa and MIT license
'SPRING' 카테고리의 다른 글
[SPRING] Spring @Value 주석에서 기본값을 올바르게 지정하는 방법은 무엇입니까? (0) | 2019.08.12 |
---|---|
[SPRING] JPA + Spring에서 예외 후 트랜잭션 롤백 (0) | 2019.08.09 |
[SPRING] @FacesComponent가있는 JSF 사용자 정의 구성 요소가 Spring Boot에 없습니다. (0) | 2019.08.06 |
[SPRING] HTTP 상태 405 - 요청 메소드 'POST'는 Spring Security가있는 Spring MVC에서 지원되지 않습니다. (0) | 2019.08.05 |
[SPRING] 스프링 부트 보안 요청한 리소스에 'Access-Control-Allow-Origin'헤더가 없습니다. Error (0) | 2019.08.01 |