티스토리 뷰
🩵컨트롤러에서 파일 다운로드 (a 태그로 클릭하여)
배경
A서버에서 API요청하여 B서버에 있는 파일을 읽어서 A서버쪽 UI(Blod 객체 사용)로 byte로 출력하려고 하니 자꾸 인코딩이 제멋대로 되며, 안의 글자들이 깨지게 되어 파일을 사용할 수 없게 됨. A,B가 같은 리눅스 환경이라 그 점을 이용해 A태그를 이용해 UI쪽 컨트롤러에서 바로 다운받을 수 있게끔 구현
+ HTTPS가 아니면 파일이 다운이 안된다고 뜨고 찾아보니 그렇다길래 HTTPS로 변경했는데 개인으로 받은거라 그런가 자꾸 인증서 문제가 생기고 제대로 돌아가지도 않아서 다시 HTTP로 변경 (우리는 총 3개의 서버를 연결해서 그런지 문제가 조금씩 있었다. 두 개의 서버는 HTTPS 괜찮을 것 같다.) 엣지에서는 또 잘 되고 그래서 그냥 HTTP로 위와 같이 구현!
내가 원하는 건 a href 태그 클릭 시 바로 다운받는 그런거였는데
내가 검색을 잘 못하는건지 내가 원하는게 잘 나오지 않아서 챗 GPT, 그리고 다른 분들 파일 다운로드 부분을 보며 구현
@Controller
@RequestMapping(value = "/api")
public class Controller {
@Value("${path}")
private String path;
@RequestMapping(value = "/endpoint", method = RequestMethod.POST, produces = "application/json; charset=UTF-8")
public ResponseEntity<?> download() {
try {
// Runtime 객체를 사용하여 쉘 스크립트 실행
Process process = Runtime.getRuntime().exec(path);
// 외부 명령어 실행이 완료될 때까지 기다림
int exitCode = process.waitFor();
// 외부 명령어 실행이 정상적으로 완료되었는지 확인
if (exitCode == 0) {
return ResponseEntity.ok("파일 이동이 성공적으로 수행되었습니다.");
} else {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body("파일 이동 중 오류가 발생하였습니다.");
}
} catch (IOException | InterruptedException e) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body("파일 이동 중 오류가 발생하였습니다.");
}
}
이건 내가 처음에 생각을 잘 못하고 만든건데 (B서버로 파일을 옮기려고 A서버의 컨트롤러)
같은 리눅스 환경인데도 B서버로 파일을 옮겨야징! 하고 만들었다..
생각해보니 그냥 경로만 넣어줘도 되는데..
저 path는 application.properties 파일에 선언해서 사용하였다 - 스프링 프레임워크 -
쉘 스크립트 path이며 쉘 스크립트 안에는 cp 명령어를 넣어 두었다.
-- 실제로 구현하고 사용
나는 저 부분을 사용하지 않았지만, 혹시 필요한 분들은 스크립트 자바에서 실행하는 방법이니 알고 있으면 좋을 것 같다.
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.util.FileCopyUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import java.io.*;
@Controller
@RequestMapping(value = "/api")
public class FileDownloadController {
@Autowired
private Service service;
@Value("${download.path}")
private String downloadPath;
@GetMapping(path = "/download")
public ResponseEntity<byte[]> fileDownload() {
// 가장 최근에 생성된 파일을 다운받기 위해 넣어둠
String fileName = service.filePath();
// 파일 이름이 비어있는 경우 not_found
if (fileName.isEmpty()) {
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
// properties 파일에 넣어둔 path 와 서비스에서 가져온 Name 합침
String filePath = downloadPath + fileName;
File file = new File(filePath);
// 파일 없거나 읽을 수 없는 경우 error
if (!file.exists() || !file.canRead()) {
log.error("File not found or not readable: " + filePath);
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
// byte 단위로 읽어옴 / 주어진 파일의 내용을 읽어와서 byte 배열에 저장
try (InputStream is = new BufferedInputStream(new FileInputStream(file))) {
byte[] fileContent = FileCopyUtils.copyToByteArray(is);
HttpHeaders headers = new HttpHeaders();
// 이건 여기서는 잘 붙여서 가는 것 같은데
// UI쪽에서 Blod 객체로 하게 되면 header에 붙질 않더라 - cors정책때문인가?
headers.setContentDispositionFormData("attachment", fileName);
headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
headers.setContentLength(file.length());
return ResponseEntity.ok().headers(headers).body(fileContent);
} catch (IOException e) {
log.error("Error reading file: " + filePath, e);
return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
}
}
}
mport org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import java.io.File;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
@Service
public class Service {
@Value("${download.path}")
private String downloadPath;
public String FilePath() {
// 디렉토리 내의 파일 리스트를 가져오기
File directory = new File(downloadPath);
File[] files = directory.listFiles();
// 파일 리스트가 없거나 디렉토리가 아닌 경우 빈 문자열 반환
if (files == null || files.length == 0) {
return "";
}
// 우리는 파일이 하나가 아니라 두 개씩 생성되었는데
// 하나는 생성날짜, 하나는 문자열로 생성이 되어 문자열 파일은 제외하기 위함
List<File> validFiles = new ArrayList<>();
for (File file : files) {
if (!file.getName().equals("문자열")) {
validFiles.add(file);
}
}
// 파일 리스트를 최신 수정일자순으로 정렬
validFiles.sort(Comparator.comparingLong(File::lastModified).reversed());
// 가장 첫 번째 파일(가장 최신 파일)의 파일명 반환
return validFiles.get(0).getName();
}
}
<a download="filename" href="`${pageContext.request.contextPath}/api/download`" >
다운로드 </a>
컨트롤러와 서비스를 적절한 이름과 적절한 path를 넣어 구현하면 된다 + a 태그사용해서 저런식으로 구현하면
클릭하면 컨트롤러를 타고 로직이 실행이 되며 이쁘게 다운이 된다.
<a href="${pageContext.request.contextPath}/api/download">
이런식으로 사용하는 것 같기도 하고 우리는 일반 jsp가 아니라 위처럼 구현했는데
맞게 조금 수정하여 구현하면 된다.
* pageContext.request.contextPath 웹에서 상대적인 경로를 나타냄 *
'JAVA' 카테고리의 다른 글
[java] UUID(Universally Unique Identifier) (0) | 2023.11.14 |
---|---|
Java 객체 (mapper.convertValue) (0) | 2023.10.26 |
공지사항
최근에 올라온 글
최근에 달린 댓글
- Total
- Today
- Yesterday
링크
TAG
- PathVariable
- 파일전송프로토콜
- computed
- 패킷캡쳐하는법
- vue.js
- selectOption
- 파라미터받는법
- centos7
- pcap저장
- invokeMethod
- 서버포트설정
- 스프링프레임워크
- 구글스프레드시트
- 프론트엔드 #
- APP SCRIPT
- 로컬에서서버
- 고유식별자
- 부트스트랩
- 개발자질문
- ReflectionTestUtils
- SCP 명령어
- vue라이프사이클
- How to generate an uuid in google sheet?
- uuid
- 개발자면접질문
- selectpicker
- springMVC #DTO #VO #DAO
- 와이어샤크문법
- nextTick
- 리눅스
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 |
글 보관함