[Cloud, Java] Controlling Your Cloud - Uploading Large Files To Oracle Object Storage

原文はこちら。
https://blogs.oracle.com/developers/controlling-your-cloud-uploading-large-files-to-oracle-object-storage

前回のエントリでは、OCI Java SDKでOracle Cloud Infrastructure(OCI)APIを操作する概要を説明しました。
Controlling Your Cloud - A Look At The Oracle Cloud Infrastructure Java SDK
http://blogs.oracle.com/developers/controlling-your-cloud-a-look-at-the-oracle-cloud-infrastructure-java-sdk
https://orablogs-jp.blogspot.com/2019/01/controlling-your-cloud-look-at-oracle.html
エントリ中で、SDKを掘り下げようとしたのは、OCI Object Storageへの大容量ファイルのアップロードを処理するためであると述べました。このエントリでは、実際にマルチパート・アップロードをやってみます。

先に述べたように、HTTP(Hypertext Transfer Protocol)はもともと大きなファイル転送を扱うことを意図しておらず、ファイル転送は通常(そして多くの場合、依然として)FTP(File Transfer Protocol)を使って処理されていました。しかし、Web開発者はグローバルに分散されたクライアントを扱っており、FTPはサーバーのセットアップ、カスタムデスクトップクライアント、さまざまなファイアウォールルール、および認証を必要とするため、最終的に大きなファイルをHTTP/Sを使って転送してしまうのです。状況が許せば、Bit Torrentがより良い解決策になるかもしれませんが、Web開発者が扱っている分散ファイルはほとんどの場合解決策にはなりません。過去数年にわたるHTTPの多くの進歩のおかげで、大きなファイル転送が扱いやすくなりました。主な進歩は、(「チャンク」または「マルチパート」ファイルアップロードとして知られる)Chunked Transfer Encodingです。
Chunked Transfer Encoding
https://en.wikipedia.org/wiki/Chunked_transfer_encoding
マルチパートアップロードに関する Oracleのサポートについては、以下のドキュメントで詳細を説明していますが、できる限り簡単に説明するとすれば、ファイルをいくつかの部分(「チャンク」)に分割し、(必要に応じて同時に)アップロードし、すべての部分がアップロードされたら元のファイルに再構築するというものです。
Using Multipart Uploads
https://docs.cloud.oracle.com/iaas/Content/Object/Tasks/usingmultipartuploads.htm
Java SDKを使ってマルチパートアップロードを実施するには、少なくとも3ステップ必要です。詳細を期したSDKのJavaDocsは以下からご覧いただけます。
Oracle Cloud Infrastructure Java SDK - 1.3.3
https://docs.cloud.oracle.com/iaas/tools/java/latest/
  1. Initiate the multipart upload
  2. Upload the individual file parts
  3. Commit the upload
SDKは上記手順の全てのためのメソッドだけでなく、既存のマルチパートアップロードのリストなどのための追加の手順のためのメソッドも提供しています。個々のファイルは最大50GBになる可能性があります。上記の3つのステップを完了するために必要なObjectClient(前の記事を参照)を使用するSDKプロセスを以下のように説明しています。
1. CreateMultipartUploadRequestDetailsインスタンスを含むCreateMultipartUploadRequestインスタンスを渡してObjectClient.createMultipartUploadを呼び出す。
ステップ1を詳説すると、「ファイルをアップロードしたい。オブジェクト名はfoo.jpg、コンテンツタイプはimage / jpegだ。後でそのファイルの異なるピースを関連付けられるよう、識別子を教えてくれるかな?」とAPIに尋ねれば、APIはCreateMultipartUploadResponseの形で識別子を返してくれる、ということです。以下がそのコード例です。
// route handler (Bootstrap.groovy):
post "/oci/upload-create", { req, res ->
    def objectName = req.queryParams("objectName")
    def contentType = req.queryParams("contentType")
    return JsonOutput.toJson( objectService.createMultipartUpload(objectName, contentType) )
}

// service method (ObjectService.groovy):
def createMultipartUpload(objectName, contentType="application/octet-stream") {
    CreateMultipartUploadDetails createMultipartUploadDetails = CreateMultipartUploadDetails.builder()
            .object(objectName)
            .contentType(contentType)
            .build()
    CreateMultipartUploadRequest createMultipartUploadRequest = CreateMultipartUploadRequest.builder()
            .namespaceName(namespaceName)
            .bucketName(bucketName)
            .createMultipartUploadDetails(createMultipartUploadDetails)
            .build()
    return objectClient.createMultipartUpload(createMultipartUploadRequest)
}
ではアップロードを作成するために、objectNameとcontentTypeという引数を渡して、/oci/upload-createを呼び出します。Postmanを使って呼び出していますが、これはブラウザでfetch()を呼び出すのと同じくらい簡単です。

これで今後の作業のためのアップロード識別子を取得しました(上図の2のuploadIdをご覧ください)。ではプロセスの2番目の手順に入ります。
2. uploadId、objectName、各パーツ用の連番(partNum)、チャンクされたファイルを含むUploadPartRequestのインスタンスを付けてObjectClient.uploadPart()を呼び出し、UploadPartResponseを受け取る。このレスポンスには、この後アップロードを完了するためにパーツの連番と共に保存しておくべきETagが含まれている。
以下は手順2のコード例です。
//route handler (Bootstrap.groovy):
post "/oci/upload-part", { req, res ->
    req.attribute("org.eclipse.jetty.multipartConfig", new MultipartConfigElement("/tmp"))
    HttpRequestWrapper reqRaw = req.raw()
    InputStream is = reqRaw.getPart("uploadPart").getInputStream()
    def objectName = req.queryParams("objectName")
    def partNum = req.queryParams("partNum").toInteger()
    def uploadId = req.queryParams("uploadId")
    return JsonOutput.toJson( objectService.uploadPart(is, objectName, uploadId, partNum) )
}

//service method (ObjectService.groovy):
def uploadPart(InputStream inputStream, String objectName, String uploadId, int partNum) {
    UploadPartRequest uploadPartRequest = UploadPartRequest.builder()
            .namespaceName(namespaceName)
            .bucketName(bucketName)
            .objectName(objectName)
            .uploadPartBody(inputStream)
            .uploadId(uploadId)
            .uploadPartNum(partNum)
            .build()
    return objectClient.uploadPart(uploadPartRequest)
}
これがPostmanのステップ2の呼び出しです。アップロードはファイルの各パートに対して1回実行されました。最後の手順で使用するため、ETag値を各パーツの連番(partNum)と共に保存します。


最後の手順3でアップロードが完了します。
3. objectName、uploadId、そしてCommitMultipartUploadPartDetailsの配列を含むCommitMultipartUploadDetailsインスタンスを含むCommitMultipartUploadRequestインスタンスを渡して、ObjectClient.commitMultipartUpload()を呼び出す。
ややこしそうに見えますが、そnSounds a bit complicated, but it's really not.  The code tells the story here:
//route handler (Bootstrap.groovy):
post "/oci/upload-commit", { req, res ->
    /*
    expects a JSON object in the request body that looks like this:
    {
        uploadId: "",
        objectName: "",
        uploads: [
            {
                partNum: 1,
                ETag: "",
            }
        ]
    }
    */
    Map body = new JsonSlurper().parseText(req.body())
    def details = []
    body.uploads.each { Map file ->
        details << CommitMultipartUploadPartDetails.builder()
                .partNum(file.partNum)
                .etag(file.ETag)
                .build()

    }

    return JsonOutput.toJson( objectService.commitMultipartUpload(body.objectName, body.uploadId, details) )
}

//service method (ObjectService.groovy):
def commitMultipartUpload(String objectName, String uploadId, List partDetails) {
    CommitMultipartUploadDetails commitMultipartUploadDetails = CommitMultipartUploadDetails.builder().partsToCommit(partDetails).build()
    CommitMultipartUploadRequest commitMultipartUploadRequest = CommitMultipartUploadRequest.builder()
            .namespaceName(namespaceName)
            .bucketName(bucketName)
            .objectName(objectName)
            .uploadId(uploadId)
            .commitMultipartUploadDetails(commitMultipartUploadDetails)
            .build()
    return objectClient.commitMultipartUpload(commitMultipartUploadRequest)
}
呼び出すと、マルチパート・アップロードのコミットの完了を確認するシンプルなレスポンスが返ってきます。Object Storageのバケットに移動すると、アップロードされ再構築されたファイルの詳細を確認できます。



そして、私たちがあらかじめ決められたURLで当該URL(もしくはバケットが公開されていれば直接)アクセスすれば、その画像を見ることができます。今回の場合は、ペットのMosesの写真です。



説明した通り、マルチパート・アップロード用のOracle SDKは、必要な手順に分類されていればかなり簡単に利用できます。適切なバックエンドサービスを利用できるのであれば、マルチパートアップロードを支援するためのフロントエンドライブラリが多数あります(今回は、MacBookのsplitコマンドを使って単純に分割しました)。

0 件のコメント:

コメントを投稿