Upload

최신 업데이트:2025-08-18 15:54:16

CDNetworks 오브젝트 스토리지에서 사용자가 직접적으로 다루는 기본 데이터 단위는 오브젝트(Object)입니다. Java SDK는 다양한 오브젝트 업로드 인터페이스를 제공하여 아래와 같은 여러 방식으로 오브젝트를 업로드할 수 있습니다.

  • 스트림 업로드
  • 파일 업로드
  • 멀티파트 업로드
  • 폼(Form) 기반 업로드

SDK는 0KB에서 최대 5GB까지 크기의 오브젝트 업로드를 지원합니다. 스트림 업로드와 파일 업로드 모두 콘텐츠 크기는 5GB를 초과할 수 없습니다. 5GB보다 큰 파일 업로드 시에는 멀티파트 업로드 기능을 사용해야 하며, 이때 각 파트의 크기는 5GB를 넘을 수 없습니다. 폼 기반 업로드는 브라우저에서 HTML Form을 통해 오브젝트를 업로드하는 방식입니다.

오브젝트 업로드 시 익명 읽기 권한을 부여하면, 업로드 성공 후 해당 오브젝트는 공개 링크를 통해 접근이 가능합니다. 오브젝트 링크 형식은 https://bucket-name.domain/folder-path/object-name 입니다. 오브젝트가 버킷의 루트 디렉터리에 저장된 경우 폴더 경로는 링크에서 생략할 수 있습니다.


1. 스트림 업로드

스트림 업로드는 java.io.InputStream을 데이터 소스로 사용합니다. WosClient.putObject를 이용하여 데이터 스트림을 업로드할 수 있습니다. 아래 예시는 스트림을 통한 업로드 방법을 보여줍니다.

// 문자열(바이트 배열) 업로드
String endPoint = "https://your-endpoint";
String ak = "*** Provide your Access Key ***";
String sk = "*** Provide your Secret Key ***";
// WosClient 인스턴스 생성
WosClient wosClient = new WosClient(ak, sk, endPoint, regionName);

String content = "Hello WOS";
wosClient.putObject("bucketname", "objectname", new ByteArrayInputStream(content.getBytes()));

// 네트워크 스트림 업로드
InputStream inputStream = new URL("http://www.a.com").openStream();
wosClient.putObject("bucketname", "objectname", inputStream);

// 파일 스트림 업로드
FileInputStream fis = new FileInputStream(new File("localfile"));  // 전체 파일 경로 지정
wosClient.putObject("bucketname", "objectname", fis);

참고:

  1. 로컬 파일의 경우 파일 스트림보다는 파일 업로드 방식을 권장합니다.
  2. 대용량 파일의 경우 멀티파트 업로드를 사용하세요.
  3. 업로드 파일 최대 크기는 5GB입니다.

2. 파일 업로드

파일 업로드는 로컬 파일을 오브젝트의 원본 데이터로 사용합니다. 아래 예시는 파일 업로드 방법을 보여줍니다.

String endPoint = "https://your-endpoint";
String ak = "*** Provide your Access Key ***";
String sk = "*** Provide your Secret Key ***";
// WosClient 인스턴스 생성
WosClient wosClient = new WosClient(ak, sk, endPoint, regionName);

wosClient.putObject("bucketname", "objectname", new File("localfile")); // 전체 파일 경로 지정

참고: 업로드 파일 최대 크기는 5GB입니다.


3. 폴더 생성

CDNetworks 오브젝트 스토리지에서는 폴더 개념이 따로 존재하지 않으며, 실제로는 모든 데이터가 오브젝트로 저장됩니다. 폴더 생성이란 /로 끝나는 이름의 0바이트 오브젝트를 생성하는 것을 의미합니다. 이렇게 생성된 오브젝트는 일반 오브젝트와 동일하게 다운로드/삭제할 수 있으며, 콘솔에서는 폴더처럼 표시됩니다.

String endPoint = "https://your-endpoint";
String ak = "*** Provide your Access Key ***";
String sk = "*** Provide your Secret Key ***";
// WosClient 인스턴스 생성
WosClient wosClient = new WosClient(ak, sk, endPoint, regionName);

final String keySuffixWithSlash = "parent_directory/";
wosClient.putObject("bucketname", keySuffixWithSlash, new ByteArrayInputStream(new byte[0]));

// 폴더 내 오브젝트 생성
wosClient.putObject("bucketname", keySuffixWithSlash + "objectname", new ByteArrayInputStream("Hello WOS".getBytes()));

참고:

  1. 폴더 생성은 이름이 /로 끝나는 0바이트 오브젝트를 만드는 것입니다.
  2. 계층 구조 폴더를 만들 때는 마지막 레벨만 생성하면 됩니다. 예) src1/src2/src3/ 생성 시 src1/src2/src3/만 생성하면 되며, 상위 디렉터리들은 별도로 만들 필요가 없습니다.

4. 오브젝트 메타데이터 설정

오브젝트 업로드 시 메타데이터를 함께 지정할 수 있습니다. 메타데이터에는 오브젝트 길이, MIME 타입, MD5 해시(유효성 검증용), 스토리지 클래스, 사용자 정의 메타데이터 등이 포함됩니다. 스트림, 파일, 멀티파트, 복사 업로드 등 모든 업로드 방식에서 메타데이터를 지정할 수 있습니다.

아래 표는 각 필드의 설명입니다:

이름 설명 기본값
Content Length
(Content-Length)
업로드 오브젝트의 길이. 파일/스트림 크기를 초과할 수 없으며, 크기를 초과할 경우 초과분은 무시됨. 실제 스트림 또는 파일 크기
MIME Type
(Content-Type)
오브젝트/파일 타입 또는 웹페이지 인코딩. 브라우저가 오브젝트를 어떻게 처리할지 결정함. application/octet-stream
MD5 Value
(Content-MD5)
오브젝트 MD5 해시(Base64 인코딩). 서버에서 업로드 데이터 무결성 검증에 사용. 설정 안 함
Storage Class 오브젝트의 스토리지 클래스. 성능 및 비용 최적화 목적. 지정하지 않으면 버킷과 동일한 값 사용. 버킷의 스토리지 클래스
Custom User Metadata 사용자 정의 속성. 오브젝트를 개인화하여 관리할 수 있음. 설정 안 함

Content-Length 설정

ObjectMetadata.setContentLength를 사용하여 오브젝트 길이 지정이 가능합니다. 예시:

ObjectMetadata metadata = new ObjectMetadata();
metadata.setContentLength(1024 * 1024L); // 1MB
wosClient.putObject("bucketname", "objectname", new File("localfile"), metadata);

MIME Type 설정

ObjectMetadata.setContentType를 사용하면 MIME 타입을 지정할 수 있습니다. 예시:

ObjectMetadata metadata = new ObjectMetadata();
metadata.setContentType("image/jpeg");
wosClient.putObject("bucketname", "objectname.jpg", new File("localimage.jpg"), metadata);

참고: MIME 타입을 지정하지 않으면, SDK가 확장자에 따라 자동 추론합니다. (예: .xmlapplication/xml, .htmltext/html 등)

MD5 값 설정

ObjectMetadata.setContentMd5로 오브젝트의 MD5 값을 지정합니다. 예시:

ObjectMetadata metadata = new ObjectMetadata();
metadata.setContentMd5("your md5 which should be encoded by base64");
wosClient.putObject("bucketname", "objectname", new File("localimage.jpg"), metadata);

참고:

  • MD5 값은 Base64로 인코딩되어야 합니다.
  • 서버는 업로드된 데이터의 MD5와 지정된 MD5가 다를 경우 업로드를 400 오류로 실패 처리합니다.
  • MD5를 설정하지 않으면 서버가 이 검증을 생략합니다.
  • MD5 값은 WosClient.base64Md5로 계산할 수 있습니다.

스토리지 클래스 설정

ObjectMetadata.setObjectStorageClass로 스토리지 클래스를 지정할 수 있습니다. 예시:

ObjectMetadata metadata = new ObjectMetadata();
metadata.setObjectStorageClass(StorageClassEnum.ia); // IA(Infrequent Access)로 저장
wosClient.putObject("bucketname", "objectname", new File("localfile"), metadata);

참고:

  • 지정하지 않으면 오브젝트의 스토리지 클래스는 버킷의 클래스와 동일합니다.
  • 스토리지 클래스는 3종류이며, 의미는 버킷의 스토리지 클래스와 같습니다.
  • Archive 클래스 오브젝트는 다운로드 전 반드시 복원이 필요합니다.

사용자 정의 메타데이터 설정

ObjectMetadata.addUserMetadata로 사용자 정의 메타데이터를 지정할 수 있습니다. 예시:

ObjectMetadata metadata = new ObjectMetadata();
metadata.addUserMetadata("property1", "property-value1");
metadata.getMetadata().put("property2", "property-value2");
wosClient.putObject("bucketname", "objectname", new File("localfile"), metadata);

참고:

  • 위 예시에서는 property1property2라는 두 개의 사용자 정의 메타데이터를 등록하였습니다.
  • 여러 개의 메타데이터 설정 가능하며, 총 크기가 8KB를 넘을 수 없습니다.
  • WosClient.getObjectMetadata를 통해 사용자 정의 메타데이터 조회가 가능합니다.
  • WosClient.getObject로 오브젝트 다운로드 시에도 메타데이터가 출력됩니다.

5. 멀티파트 업로드

멀티파트 업로드는 아래와 같은 상황에서 권장됩니다.

  • 100MB 이상의 대용량 파일 업로드
  • 불안정한 네트워크/제한된 대역폭 환경
  • 업로드 파일 크기를 미리 알 수 없는 경우

장점:

  • 병렬 처리를 통해 업로드 속도/성능 향상
  • 업로드 도중 네트워크 단절 시 이어서 업로드 가능(Resume)
  • 업로드 작업을 필요에 따라 중단/재개 가능(자동 만료 없음, 명시적으로 취소 필요)
  • 전체 크기를 미리 알 필요 없음

구현 단계:

  1. 초기화 (WosClient.initiateMultipartUpload)
  2. 파트 업로드 (순차/병렬, WosClient.uploadPart)
  3. 완성(WosClient.completeMultipartUpload), 혹은 중단(WosClient.abortMultipartUpload)

초기화

먼저 멀티파트 업로드를 초기화하고, 관련 파라미터를 설정하여 Upload ID를 획득합니다. 이 Upload ID는 이후 업로드/취소/조회 등에 반드시 필요합니다.

InitiateMultipartUploadRequest request = new InitiateMultipartUploadRequest("bucketname", "objectname");
ObjectMetadata metadata = new ObjectMetadata();
metadata.addUserMetadata("property", "property-value");
metadata.setContentType("text/plain");
request.setMetadata(metadata);
InitiateMultipartUploadResult result = wosClient.initiateMultipartUpload(request);

String uploadId = result.getUploadId();
System.out.println("\t" + uploadId);

참고:

  • InitiateMultipartUploadRequest를 통해 오브젝트 이름 및 버킷 지정
  • MIME 타입, 스토리지 클래스, 사용자 지정 메타데이터 등 지정 가능
  • InitiateMultipartUploadResult.getUploadId로 후속 작업에 고유하게 사용되는 Upload ID 확인

파트 업로드

초기화 이후 오브젝트 이름 및 Upload ID를 사용하여 파트를 업로드합니다. 각 파트는 파트 번호(1~10000)로 고유 식별되며, 파트 번호의 오름차순으로 병합됩니다. 동일 파트 번호로 다시 업로드할 경우 기존 내용이 덮어쓰여집니다. 마지막 파트를 제외한 모든 파트는 100KB~5GB, 마지막 파트는 0~5GB여야 하며, 각 파트는 순서 무관하게, 혹은 서로 다른 프로세스, 머신에서 병렬 업로드할 수 있습니다.

List<PartEtag> partEtags = new ArrayList<PartEtag>();
// 첫 번째 파트 업로드
UploadPartRequest request = new UploadPartRequest("bucketname", "objectname");
request.setUploadId(uploadId);
request.setPartNumber(1);
request.setFile(new File("localfile"));
request.setPartSize(5 * 1024 * 1024L);
UploadPartResult result = wosClient.uploadPart(request);
partEtags.add(new PartEtag(result.getEtag(), result.getPartNumber()));

// 두 번째 파트 업로드
request = new UploadPartRequest("bucketname", "objectname");
request.setUploadId(uploadId);
request.setPartNumber(2);
request.setFile(new File("localfile"));
request.setOffset(5 * 1024 * 1024L);
request.setPartSize(5 * 1024 * 1024L);
result = wosClient.uploadPart(request);
partEtags.add(new PartEtag(result.getEtag(), result.getPartNumber()));

참고:

  • 마지막 파트를 제외한 모든 파트는 100KB 이상이어야 합니다(최종 병합 시 검증).
  • 서버에서 각 파트의 ETag(MD5)가 반환됩니다.
  • 무결성 검증 필요 시 UploadPartRequest.setAttachMd5(true)로 SDK가 MD5 자동 생성/검증(로컬 파일만 지원)하도록 할 수 있습니다. (ContentMd5 직접 지정시 setAttachMd5는 무시)
  • 파트 번호 범위는 1~10000. 벗어나면 400 오류 발생.

멀티파트 업로드 완료

모든 파트 업로드가 끝나면 각 파트 번호와 ETag를 모아 최종 병합 API를 호출합니다. 서버는 모든 파트를 검증한 후 업로드를 마칩니다.

List<PartEtag> partEtags = new ArrayList<PartEtag>();
// partEtags에 각 파트의 번호 및 ETag를 채움

CompleteMultipartUploadRequest completeRequest = new CompleteMultipartUploadRequest("bucketname", "objectname", uploadId, partEtags);

wosClient.completeMultipartUpload(completeRequest);

참고:

  • partEtags에는 업로드한 모든 파트의 번호/ETag가 포함되어야 하며, 연속되지 않아도 무방합니다.

병렬 멀티파트 업로드

멀티파트 업로드의 주된 장점은 대용량 파일의 더 빠르고 안정적인 업로드입니다. 아래는 병렬 업로드 방법 예시입니다.

// 초기화는 생략
ExecutorService executorService = Executors.newFixedThreadPool(20);
final File largeFile = new File("localfile");

long partSize = 100 * 1024 * 1024L;
long fileSize = largeFile.length();
long partCount = fileSize % partSize == 0 ? fileSize / partSize : fileSize / partSize + 1;
final List<PartEtag> partEtags = Collections.synchronizedList(new ArrayList<PartEtag>());

for (int i = 0; i < partCount; i++) {
    final long offset = i * partSize;
    final long currPartSize = (i + 1 == partCount) ? fileSize - offset : partSize;
    final int partNumber = i + 1;
    executorService.execute(() -> {
        UploadPartRequest uploadPartRequest = new UploadPartRequest();
        uploadPartRequest.setBucketName(bucketName);
        uploadPartRequest.setObjectKey(objectKey);
        uploadPartRequest.setUploadId(uploadId);
        uploadPartRequest.setFile(largeFile);
        uploadPartRequest.setPartSize(currPartSize);
        uploadPartRequest.setOffset(offset);
        uploadPartRequest.setPartNumber(partNumber);

        try {
            UploadPartResult uploadPartResult = wosClient.uploadPart(uploadPartRequest);
            System.out.println("Part#" + partNumber + " done\n");
            partEtags.add(new PartEtag(uploadPartResult.getEtag(), uploadPartResult.getPartNumber()));
        } catch (WosException e) {
            e.printStackTrace();
        }
    });
}
executorService.shutdown();
while (!executorService.isTerminated()) {
    executorService.awaitTermination(5, TimeUnit.SECONDS);
}
// 병합
CompleteMultipartUploadRequest completeMultipartUploadRequest = new CompleteMultipartUploadRequest(bucketName, objectKey, uploadId, partEtags);
wosClient.completeMultipartUpload(completeMultipartUploadRequest);

참고: 멀티파트 업로드에서는 각 파트마다 UploadPartRequest.setOffset, setPartSize로 오프셋/길이 지정이 필요합니다.

멀티파트 업로드 취소

진행 중인 멀티파트 업로드 작업은 중단(취소)할 수 있습니다. 중단 이후 해당 Upload ID는 무효화되고, 업로드된 파트 데이터도 삭제됩니다.

저장 공간을 절약하려면 실패하거나 진행 중인 멀티파트 작업을 직접 중단 처리할 수 있습니다.

AbortMultipartUploadRequest request = new AbortMultipartUploadRequest("bucketname", "objectname", uploadId);
wosClient.abortMultipartUpload(request);

파트 목록 조회

WosClient.listParts로 특정 멀티파트 업로드 작업에 대하여 업로드된 파트들의 정보(번호, 크기, ETag 등 전체)를 조회할 수 있습니다.

ListPartsRequest request = new ListPartsRequest("bucketname", "objectname");
request.setUploadId(uploadId);
ListPartsResult result = wosClient.listParts(request);

for (Multipart part : result.getMultipartList()) {
    System.out.println("\t" + part.getPartNumber());   // 파트 번호
    System.out.println("\t" + part.getSize());         // 파트 크기
    System.out.println("\t" + part.getEtag());         // 파트 ETag
    System.out.println("\t" + part.getLastModified()); // 업로드 일시
}

참고:

  • 한 번에 최대 1,000개 파트가 반환됩니다. 1,000개 초과 시 ListPartsResult.isTruncated()가 true를 반환하며, getNextPartNumberMarker()로 페이징 처리해야 합니다.
  • 1,000개가 넘는 파트는 반복 호출로 모두 조회할 수 있습니다.

전체 파트 목록(페이징 처리 예시)

do {
    result = wosClient.listParts(request);
    for(Multipart part : result.getMultipartList()){
        // 파트 정보 출력
    }
    request.setPartNumberMarker(Integer.parseInt(result.getNextPartNumberMarker()));
} while (result.isTruncated());

멀티파트 업로드 작업 목록 조회

WosClient.listMultipartUploads를 사용하면 현재 진행 중인 모든 멀티파트 업로드 작업(Upload ID 리스트 포함)을 조회할 수 있습니다.

ListMultipartUploadsRequest request = new ListMultipartUploadsRequest("bucketname");
MultipartUploadListing result = wosClient.listMultipartUploads(request);
for (MultipartUpload upload : result.getMultipartTaskList()) {
    System.out.println("\t" + upload.getUploadId());
    System.out.println("\t" + upload.getObjectKey());
    System.out.println("\t" + upload.getInitiatedDate());
}

참고:

  • 한 번에 최대 1,000개 작업이 반환됩니다. 페이징 시 isTruncated(), getNextKeyMarker(), getNextUploadIdMarker() 사용

페이징 처리 예시:

do {
    result = wosClient.listMultipartUploads(request);
    for (MultipartUpload upload : result.getMultipartTaskList()) {
        // 업로드 정보 출력
    }
    request.setKeyMarker(result.getNextKeyMarker());
    request.setUploadIdMarker(result.getNextUploadIdMarker());
} while (result.isTruncated());

6. 폼(Form) 기반 업로드

폼 기반 업로드는 HTML 양식(Form)을 사용하여 오브젝트를 업로드하는 방식입니다. 업로드 가능한 최대 오브젝트 크기는 5GB입니다.

WosClient.createPostSignature를 통해 폼 업로드에 필요한 파라미터를 생성할 수 있습니다. 예시는 PostObjectSample을 참고하세요.

진행 절차:

  • WosClient.createPostSignature를 호출해 인증 정보 생성
  • HTML Form 준비
  • 생성된 파라미터를 form 필드 값에 채움
  • 파일 선택 후 업로드

WosClient.createPostSignature로 생성되는 파라미터:

  • policy (form의 policy 필드)
  • signature (form의 signature 필드)

폼 파라미터 생성 예시:

PostSignatureRequest request = new PostSignatureRequest();
Map<String, Object> formParams = new HashMap<String, Object>();
formParams.put("x-wos-acl", "public-read");             // ACL 설정
formParams.put("content-type", "text/plain");           // MIME 타입 설정

request.setFormParams(formParams);
request.setExpires(3600);                               // 유효기간(초)
PostSignatureResponse response = wosClient.createPostSignature(request);

formParams.put("key", objectKey);
formParams.put("policy", response.getPolicy());
formParams.put("accesskeyid", ak);
formParams.put("x-wos-credential", response.getCredential());
formParams.put("x-wos-algorithm", response.getAlgorithm());
formParams.put("bucket", bucketName);
formParams.put("x-wos-date", response.getDate());
formParams.put("x-wos-signature", response.getSignature());

System.out.println("\t" + response.getPolicy());
System.out.println("\t" + response.getSignature());

HTML Form 예시:

<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</head>
<body>
<form action="http://bucketname.your-endpoint/" method="post" enctype="multipart/form-data">
Object key
<input type="text" name="key" value="objectname" />
<p>
ACL
<input type="text" name="x-wos-acl" value="public-read" />
<p>
Content-Type
<input type="text" name="content-type" value="text/plain" />
<p>
<input type="hidden" name="policy" value="*** Provide your policy ***" />
<input type="hidden" name="AccessKeyId" value="*** Provide your access key ***"/>
<input type="hidden" name="signature" value="*** Provide your signature ***"/>
<input name="file" type="file" />
<input name="submit" value="Upload" type="submit" />
</form>
</body>
</html>

참고:

  • HTML 폼의 policysignature 값은 WosClient.createPostSignature 반환값으로 채우면 됩니다.
  • 샘플 HTML 폼(PostDemo)을 다운로드하여 데모로 사용할 수 있습니다.
이 문서의 내용이 도움이 되었습니까?
아니오
정상적으로 제출되었습니다.피드백을 주셔서 감사합니다.앞으로도 개선을 위해 노력하겠습니다.