Python을 이용한 AWS S3 멀티파트 업로드

2018. 9. 21. 00:03서버 프로그래밍

AWS S3와 같은 분산 병렬 처리를 위한 파일 시스템에는 기본적으로 append와 같은 파일 수정 기능이 없다. 따라서, 실시간으로 수신되는 작은 크기의 데이터를 일정 크기로 버퍼링 하면서 업로드할 수 있는 방법이 필요하다. 구글링 하다보면 자주 걸리는 블로그에서 "멀티파트 업로딩"이라는 힌트를 얻었다.


http://bcho.tistory.com/778

S3에는 파일을 업로드 할때, multipart uploading이라는 기능을 제공한다. 파일을 하나의 Connection에서 쭈욱 올리는 것이 일반적인 방법이라면 파일을 여러개의 블럭으로 나눠서 동시에 여러개의 Connection을 통해서 업로드 하는 방법이다. 이 경우 업로드가 Parallel하게 이루어지기 때문에 상당 부분의 성능 향상을 가지고 올 수 있다.


친절하게 Python의 boto3를 이용하여 멀티파트 업로딩을 구현한 예제가 있다. resource가 아니라 client 객체를 이용하는 것에 주의.

https://gist.github.com/teasherm/bb73f21ed2f3b46bc1c2ca48ec2c1cf5


* 주의할 사항은, 멀티파트 업로드의 경우 각 파트가 최소 5MB 이상이 되어야 하는 제약이 있다. 맨마지막 파트는 5MB 이하라도 상관없다.

https://docs.aws.amazon.com/ko_kr/AmazonS3/latest/dev/qfacts.html

Amazon S3 멀티파트 업로드 제한

다음 표에 멀티파트 업로드의 주요 사양이 나와 있습니다. 자세한 내용은 멀티파트 업로드 개요 단원을 참조하십시오.

항목사양
최대 객체 크기5TB
업로드당 최대 부분 개수10,000개
부분 번호1 ~ 10,000(포함)
부분 크기5MB ~ 5GB(마지막 부분은 < 5MB도 가능)
파트 목록 조회 요청에 대해 반환되는 최대 부분 개수1000
멀티파트 업로드 나열 요청에서 반환되는 최대 멀티파트 업로드 개수1000


AWS 공식 레퍼런스는 엄청나게 방대하지만 딱히 눈에 들어오도록 만들어져 있지 않아서 그저 참고용일 뿐.

https://aws.amazon.com/ko/premiumsupport/knowledge-center/s3-multipart-upload-cli/

https://docs.aws.amazon.com/sdk-for-php/v3/developer-guide/s3-multipart-upload.html

https://docs.aws.amazon.com/ko_kr/aws-mobile/latest/developerguide/how-to-transfer-files-with-transfer-utility.html

https://docs.aws.amazon.com/ko_kr/AmazonS3/latest/dev/LLlistMPuploadsJava.html


S3에 큰 블록을 저장하는 것은 아무래도 시간이 걸리기 때문에 쓰레드로 구현해야 한다. Python 쓰레드는 다음을 참고했다.

http://www.tutorialspoint.com/python/python_multithreading.htm

#!/usr/bin/python

import threading
import time

exitFlag = 0

class myThread (threading.Thread):
   def __init__(self, threadID, name, counter):
      threading.Thread.__init__(self)
      self.threadID = threadID
      self.name = name
      self.counter = counter
   def run(self):
      print "Starting " + self.name
      print_time(self.name, 5, self.counter)
      print "Exiting " + self.name

def print_time(threadName, counter, delay):
   while counter:
      if exitFlag:
         threadName.exit()
      time.sleep(delay)
      print "%s: %s" % (threadName, time.ctime(time.time()))
      counter -= 1

# Create new threads
thread1 = myThread(1, "Thread-1", 1)
thread2 = myThread(2, "Thread-2", 2)

# Start new Threads
thread1.start()
thread2.start()

print "Exiting Main Thread"