반응형
1. HDFS WebHDFS란?
WebHDFS는 HTTP 프로토콜을 사용하여 HDFS에 접근할 수 있도록 하는 API다.
이를 활용하면 Java의 Hadoop 라이브러리를 직접 사용하지 않고도 RESTful API를 통해 데이터를 다룰 수 있다.
WebHDFS API 주요 특징
- HTTP 요청을 통해 HDFS 파일 읽기 및 쓰기 가능
- 보안 설정을 통해 사용자 인증 및 권한 관리 지원
- 데이터 노드(Datanode)로 리디렉션(307 Redirect)하여 효율적으로 파일 전송
2. HDFS WebHDFS API를 활용한 파일 다운로드 개요
HDFS에서 파일을 다운로드하는 기본적인 과정은 다음과 같다:
- GET 요청을 WebHDFS에 보낸다.
- HDFS는 307 Temporary Redirect 응답을 반환하며, 데이터 노드(Datanode) URL을 제공한다.
- 프론트엔드 또는 백엔드는 리디렉션된 URL을 따라 파일을 다운로드한다.
Tip: noredirect=true 옵션을 추가하면 307 리디렉션 없이 직접 다운로드 URL을 반환할 수 있다.
3. Spring Boot에서 HDFS 파일 다운로드 구현
Spring Boot 백엔드에서 HDFS 파일 다운로드를 처리하기 위해 FeignClient를 활용.
HDFS FeignClient 설정
@FeignClient(name = "hdfsClient", url = "${hdfs.base-url}")
public interface HdfsFeignClient {
// 파일 존재 여부 확인
@GetMapping(value = "/webhdfs/v1/{filePath}", produces = MediaType.APPLICATION_JSON_VALUE)
ResponseEntity<Map<String, Object>> checkFileExists(
@PathVariable("filePath") String filePath,
@RequestParam("op") String operation,
@RequestParam("user.name") String userName
);
// 파일 다운로드 (리디렉션 없이 JSON으로 URL 받기)
@GetMapping(value = "/webhdfs/v1/{filePath}", produces = MediaType.APPLICATION_JSON_VALUE)
ResponseEntity<Map<String, String>> getDownloadUrl(
@PathVariable("filePath") String filePath,
@RequestParam("op") String operation,
@RequestParam("user.name") String userName,
@RequestParam("noredirect") boolean noRedirect
);
}
파일 다운로드 서비스 구현
@Service
public class HdfsFileService {
@Autowired
private HdfsFeignClient hdfsFeignClient;
private static final String HDFS_USER = "hdfs";
public ResponseEntity<Resource> downloadFile(String filePath) {
// 1️⃣ 파일 존재 여부 확인
ResponseEntity<Map<String, Object>> checkResponse = hdfsFeignClient.checkFileExists(filePath, "GETFILESTATUS", HDFS_USER);
if (checkResponse.getStatusCode() == HttpStatus.NOT_FOUND) {
throw new RuntimeException("파일이 존재하지 않습니다: " + filePath);
}
// 2️⃣ WebHDFS에서 직접 다운로드 URL 가져오기
ResponseEntity<Map<String, String>> response = hdfsFeignClient.getDownloadUrl(filePath, "OPEN", HDFS_USER, true);
if (response.getStatusCode() != HttpStatus.OK || !response.getBody().containsKey("Location")) {
throw new RuntimeException("HDFS 다운로드 URL을 가져오지 못했습니다.");
}
String downloadUrl = response.getBody().get("Location");
// 3️⃣ URL을 통해 파일 다운로드 및 스트림 반환
try {
HttpURLConnection connection = (HttpURLConnection) new URL(downloadUrl).openConnection();
connection.setRequestMethod("GET");
InputStream inputStream = connection.getInputStream();
InputStreamResource resource = new InputStreamResource(inputStream);
return ResponseEntity.ok()
.contentType(MediaType.APPLICATION_OCTET_STREAM)
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=" + filePath.substring(filePath.lastIndexOf("/") + 1))
.body(resource);
} catch (IOException e) {
throw new RuntimeException("HDFS 파일 다운로드 실패", e);
}
}
}
컨트롤러
@RestController
@RequestMapping("/hdfs")
public class HdfsFileController {
@Autowired
private HdfsFileService hdfsFileService;
@GetMapping("/download/{filePath}")
public ResponseEntity<Resource> downloadHdfsFile(@PathVariable String filePath) {
return hdfsFileService.downloadFile(filePath);
}
}
4. Vue.js 프론트엔드에서 파일 다운로드 처리
Vue.js 프론트엔드는 axios를 활용하여 백엔드에서 파일을 가져오고, Blob 객체를 사용해 사용자에게 다운로드 링크를 제공한다.
파일 다운로드 함수
import axios from "axios";
const downloadFile = async (filePath) => {
try {
const response = await axios.get(`/hdfs/download/${filePath}`, {
responseType: "blob"
});
// 파일 다운로드 처리
const blob = new Blob([response.data]);
const url = window.URL.createObjectURL(blob);
const link = document.createElement("a");
// Content-Disposition 헤더에서 파일명 가져오기
let fileName = "downloaded_file";
const contentDisposition = response.headers["content-disposition"];
if (contentDisposition) {
const matches = contentDisposition.match(/filename\*?=(UTF-8'')?(.+)/);
if (matches) {
fileName = decodeURIComponent(matches[2].replace(/["']/g, ""));
}
}
link.href = url;
link.setAttribute("download", fileName);
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
window.URL.revokeObjectURL(url);
} catch (error) {
console.error("파일 다운로드 실패", error);
}
};
5. 정리 및 최적화 고려 사항
✅ noredirect=true를 사용하여 WebHDFS에서 직접 다운로드 URL을 받을 수 있음
✅ 대용량 파일 다운로드 시 InputStreamResource를 활용하여 메모리 효율을 높임
✅ Vue.js에서 Content-Disposition 헤더를 파싱하여 파일명을 정상적으로 표시
✅ 보안 및 성능 최적화를 위해 캐싱 전략, 파일 크기 제한, 인증 방식을 고려해야 함
반응형
'Backend > Spring(활용)' 카테고리의 다른 글
컴포넌트 스캔의 필터 활용 - Spring (0) | 2023.10.27 |
---|---|
의도적으로 동일 타입의 스프링 빈이 다 필요한 경우 - Spring (1) | 2023.10.27 |
같은 타입 빈이 여러 개인 경우 - Spring (0) | 2023.10.27 |
사용자 정의 애노테이션 - Spring (0) | 2023.10.27 |