반응형

오늘은 Spring Boot RESTful API를 사용하여 서버에 저장된 파일을 클라이언트로 다운로드하는 방법을 알아보겠습니다. 파일 다운로드는 보고서나 이미지, 문서 등을 사용자에게 제공할 때 주로 사용됩니다.


1. 파일 다운로드를 위한 기본 설정

(1) 파일 저장 디렉토리 설정

application.properties에 이미 설정된 파일 저장 경로를 기반으로 파일을 다운로드합니다. 아래 설정은 동일합니다.

file.upload-dir=uploads/

2. 파일 다운로드 API 구현

(1) Controller 작성

ResponseEntity를 사용하여 파일을 클라이언트에 전송합니다.

import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.Resource;
import org.springframework.core.io.UrlResource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.net.MalformedURLException;
import java.nio.file.Path;
import java.nio.file.Paths;

@RestController
@RequestMapping("/api/files")
public class FileDownloadController {

    @Value("${file.upload-dir}")
    private String uploadDir;

    @GetMapping("/download/{filename}")
    public ResponseEntity<Resource> downloadFile(@PathVariable String filename) {
        try {
            // 파일 경로 설정
            Path filePath = Paths.get(uploadDir).resolve(filename).normalize();
            Resource resource = new UrlResource(filePath.toUri());

            if (!resource.exists()) {
                return ResponseEntity.notFound().build();
            }

            // 파일 다운로드 헤더 설정
            return ResponseEntity.ok()
                    .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + resource.getFilename() + "\"")
                    .body(resource);
        } catch (MalformedURLException e) {
            return ResponseEntity.badRequest().build();
        }
    }
}

3. 파일 다운로드 API 테스트

(1) Postman으로 테스트

  • Endpoint: GET /api/files/download/{filename}
  • Headers: 필요 없음
  • Response: 서버에 저장된 파일이 다운로드됩니다.

(2) 브라우저에서 테스트

URL에 http://localhost:8080/api/files/download/example.txt와 같이 직접 접근하면 브라우저에서 파일이 다운로드됩니다.


4. 예외 처리

파일이 존재하지 않거나 접근할 수 없는 경우를 대비하여 적절한 예외 처리를 추가합니다.

if (!resource.exists() || !resource.isReadable()) {
    return ResponseEntity.notFound().build();
}

5. 파일 접근 보안 강화

파일 접근 경로를 제한하여 서버 파일 시스템의 보안을 강화합니다. 사용자가 다운로드 요청 시 경로 탐색 공격을 시도할 수 있으므로 normalize()를 호출해 경로를 정규화합니다.

Path filePath = Paths.get(uploadDir).resolve(filename).normalize();

(1) 파일 확장자 검증

허용된 파일 확장자만 다운로드 가능하도록 검증합니다.

private boolean isValidFileType(String filename) {
    String[] allowedExtensions = {"jpg", "png", "pdf", "txt"};
    String fileExtension = filename.substring(filename.lastIndexOf(".") + 1);
    for (String extension : allowedExtensions) {
        if (extension.equalsIgnoreCase(fileExtension)) {
            return true;
        }
    }
    return false;
}

컨트롤러에서 파일 검증 로직을 추가합니다.

if (!isValidFileType(filename)) {
    return ResponseEntity.badRequest().body(null);
}

6. 클라이언트의 파일 저장

파일 다운로드 시 브라우저 또는 클라이언트는 attachment 헤더에 설정된 파일명을 사용하여 파일을 저장합니다. 이를 통해 원본 파일명을 유지할 수 있습니다.


7. 파일 업로드 및 다운로드 통합 테스트

(1) 업로드 후 다운로드 테스트

  1. 업로드 API(/api/files/upload)로 파일을 업로드합니다.
  2. 다운로드 API(/api/files/download/{filename})로 업로드된 파일을 다운로드합니다.
  3. 다운로드된 파일의 내용을 확인하여 정확성을 검증합니다.

실습 결과

파일 업로드와 다운로드 기능을 모두 구현하였으며, 적절한 예외 처리와 보안 조치를 통해 안정적인 RESTful API를 완성할 수 있습니다.

반응형

+ Recent posts