O Object Storage Service (OSS) oferece o recurso de upload multipart para objetos grandes. Esse processo consiste em dividir um objeto grande em partes menores, enviá-las de forma independente e, em seguida, chamar a API CompleteMultipartUpload para combinar essas partes em um único objeto. Assim, é possível fazer uploads retomáveis.
Observações de uso
Antes de executar o código de exemplo deste tópico, crie uma instância OSSClient usando métodos como nome de domínio personalizado ou Security Token Service (STS). Para mais informações, consulte Initialization (Android SDK).
Processo de upload multipart
Um upload multipart é composto pelas três etapas a seguir:
-
Inicie um evento de upload multipart.
Chame o método oss.initMultipartUpload. Em seguida, o OSS retorna um uploadId globalmente único.
-
Envie as partes.
Chame o método oss.uploadPart para enviar cada parte.
NotaO número da parte identifica a posição dela dentro do objeto. Ao enviar dados com o mesmo número de parte, a parte existente é sobrescrita.
O OSS retorna o hash MD5 dos dados da parte recebida no cabeçalho ETag da resposta.
O OSS calcula o hash MD5 dos dados enviados e compara com o hash calculado pelo SDK. Se houver divergência, o OSS retorna o código de erro InvalidDigest.
-
Conclua o upload multipart.
Após enviar todas as partes, chame o método oss.CompleteMultipartUpload para combiná-las em um objeto completo.
Exemplo completo de upload multipart
O exemplo a seguir mostra o processo completo de upload multipart:
// Specify the bucket name, for example, examplebucket.
String bucketName = "examplebucket";
// Specify the full path of the object, for example, exampledir/exampleobject.txt. The full path of the object cannot contain the bucket name.
String objectName = "exampledir/exampleobject.txt";
// Specify the full path of the local file, for example, /storage/emulated/0/oss/examplefile.txt.
String localFilepath = "/storage/emulated/0/oss/examplefile.txt";
// Initialize the multipart upload.
InitiateMultipartUploadRequest init = new InitiateMultipartUploadRequest(bucketName, objectName);
InitiateMultipartUploadResult initResult = oss.initMultipartUpload(init);
// The uploadId is returned.
String uploadId = initResult.getUploadId();
// Use the uploadId to cancel the multipart upload event or list the uploaded parts.
// To cancel the multipart upload event based on the uploadId, obtain the uploadId after you call InitiateMultipartUpload to initialize the multipart upload.
// To list the uploaded parts based on the uploadId, obtain the uploadId after you call InitiateMultipartUpload to initialize the multipart upload and before you call CompleteMultipartUpload to complete the multipart upload.
// Log.d("uploadId", uploadId);
// Set the size of a single part in bytes. The minimum size of a part is 100 KB, and the maximum size is 5 GB. The size of the last part can be less than 100 KB.
int partCount = 100 * 1024;
// Upload parts.
List<PartETag> partETags = new ArrayList<>();
for (int i = 1; i < 5; i++) {
byte[] data = new byte[partCount];
RandomAccessFile raf = new RandomAccessFile(localFilepath, "r");
long skip = (i-1) * partCount;
raf.seek(skip);
raf.readFully(data, 0, partCount);
UploadPartRequest uploadPart = new UploadPartRequest();
uploadPart.setBucketName(bucketName);
uploadPart.setObjectKey(objectName);
uploadPart.setUploadId(uploadId);
// Set the part number, which starts from 1. Each uploaded part has a part number that ranges from 1 to 10,000.
uploadPart.setPartNumber(i);
uploadPart.setPartContent(data);
try {
UploadPartResult result = oss.uploadPart(uploadPart);
PartETag partETag = new PartETag(uploadPart.getPartNumber(), result.getETag());
partETags.add(partETag);
} catch (ServiceException serviceException) {
OSSLog.logError(serviceException.getErrorCode());
}
}
Collections.sort(partETags, new Comparator<PartETag>() {
@Override
public int compare(PartETag lhs, PartETag rhs) {
if (lhs.getPartNumber() < rhs.getPartNumber()) {
return -1;
} else if (lhs.getPartNumber() > rhs.getPartNumber()) {
return 1;
} else {
return 0;
}
}
});
// Complete the multipart upload.
CompleteMultipartUploadRequest complete = new CompleteMultipartUploadRequest(bucketName, objectName, uploadId, partETags);
// Upload callback. When you complete the multipart upload request, you can set the CALLBACK_SERVER parameter. After the request is complete, a callback request is sent to the specified server address. You can view the server callback result from completeResult.getServerCallbackReturnBody() in the returned result.
complete.setCallbackParam(new HashMap<String, String>() {
{
put("callbackUrl", CALLBACK_SERVER); // Change the value to your server address.
put("callbackBody", "test");
}
});
CompleteMultipartUploadResult completeResult = oss.completeMultipartUpload(complete);
OSSLog.logError("-------------- serverCallback: " + completeResult.getServerCallbackReturnBody());
O código anterior chama uploadPart para enviar cada parte.
Cada requisição de upload de parte deve especificar um uploadId e um partNumber. O valor de partNumber deve estar entre 1 e 10.000. Se o partNumber estiver fora desse intervalo, o OSS retorna o código de erro InvalidArgument.
Ao chamar uploadPart, todas as partes, exceto a última, devem ter mais de 100 KB. O OSS só verifica o tamanho das partes ao concluir o upload multipart.
Antes de enviar cada parte, posicione o stream no início dos dados daquela parte.
Após o envio de cada parte, a resposta do OSS contém um valor de ETag para aquela parte. O valor do ETag é o hash MD5 dos dados da parte. Combine o valor do ETag com o número da parte em um PartETag e armazene-o para concluir o upload multipart.
Upload multipart de um arquivo local
É possível enviar um arquivo local para o OSS usando upload multipart síncrono ou assíncrono.
A seção anterior apresenta um exemplo completo que mostra o processo de upload multipart passo a passo. O código desta seção, para envio de um arquivo local, encapsula esse processo. Basta usar MultipartUploadRequest para executar um upload multipart.
-
Enviar um arquivo local usando a operação de upload multipart síncrono
O código a seguir mostra como enviar sincronamente o arquivo examplefile.txt para o objeto exampleobject.txt na pasta exampledir do bucket examplebucket.
// Specify the bucket name, for example, examplebucket. String bucketName = "examplebucket"; // Specify the full path of the object, for example, exampledir/exampleobject.txt. The full path of the object cannot contain the bucket name. String objectName = "exampledir/exampleobject.txt"; // Specify the full path of the local file, for example, /storage/emulated/0/oss/examplefile.txt. String localFilepath = "/storage/emulated/0/oss/examplefile.txt"; ObjectMetadata meta = new ObjectMetadata(); // Set file metadata. meta.setHeader("x-oss-object-acl", "public-read-write"); MultipartUploadRequest rq = new MultipartUploadRequest(bucketName, objectName, localFilepath, meta); // Set the part size. The default part size is 256 KB. The minimum part size is 100 KB. rq.setPartSize(1024 * 1024); rq.setProgressCallback(new OSSProgressCallback<MultipartUploadRequest>() { @Override public void onProgress(MultipartUploadRequest request, long currentSize, long totalSize) { OSSLog.logDebug("[testMultipartUpload] - " + currentSize + " " + totalSize, false); } }); CompleteMultipartUploadResult result = oss.multipartUpload(rq);No armazenamento com escopo (scoped storage) do Android 10 ou posterior, você pode usar a URI de um arquivo para enviá-lo ao OSS.
// Specify the bucket name, for example, examplebucket. String bucketName = "examplebucket"; // Specify the full path of the object, for example, exampledir/exampleobject.txt. The full path of the object cannot contain the bucket name. String objectName = "exampledir/exampleobject.txt"; ObjectMetadata meta = new ObjectMetadata(); // Set file metadata. meta.setHeader("x-oss-object-acl", "public-read-write"); MultipartUploadRequest rq = new MultipartUploadRequest(bucketName, objectName, fileUri, meta); // Set the part size. The default part size is 256 KB. The minimum part size is 100 KB. rq.setPartSize(1024 * 1024); rq.setProgressCallback(new OSSProgressCallback<MultipartUploadRequest>() { @Override public void onProgress(MultipartUploadRequest request, long currentSize, long totalSize) { OSSLog.logDebug("[testMultipartUpload] - " + currentSize + " " + totalSize, false); } }); CompleteMultipartUploadResult result = oss.multipartUpload(rq); -
Chamar uma API assíncrona para executar um upload multipart de um arquivo local
O código a seguir mostra como enviar assincronamente o arquivo examplefile.txt para a pasta exampledir no bucket examplebucket, salvando-o como o objeto exampleobject.txt.
// Specify the bucket name, for example, examplebucket. String bucketName = "examplebucket"; // Specify the full path of the object, for example, exampledir/exampleobject.txt. The full path of the object cannot contain the bucket name. String objectName = "exampledir/exampleobject.txt"; // Specify the full path of the local file, for example, /storage/emulated/0/oss/examplefile.txt. String localFilepath = "/storage/emulated/0/oss/examplefile.txt"; MultipartUploadRequest request = new MultipartUploadRequest(bucketName, objectName, localFilepath); request.setProgressCallback(new OSSProgressCallback<MultipartUploadRequest>() { @Override public void onProgress(MultipartUploadRequest request, long currentSize, long totalSize) { OSSLog.logDebug("[testMultipartUpload] - " + currentSize + " " + totalSize, false); } }); OSSAsyncTask task = oss.asyncMultipartUpload(request, new OSSCompletedCallback<MultipartUploadRequest, CompleteMultipartUploadResult>() { @Override public void onSuccess(MultipartUploadRequest request, CompleteMultipartUploadResult result) { OSSLog.logInfo(result.getServerCallbackReturnBody()); } @Override public void onFailure(MultipartUploadRequest request, ClientException clientException, ServiceException serviceException) { OSSLog.logError(serviceException.getRawMessage()); } }); //Thread.sleep(100); // Cancel the multipart upload. //task.cancel(); task.waitUntilFinished();No armazenamento com escopo (scoped storage) do Android 10 ou posterior, você pode usar a URI de um arquivo para enviá-lo ao OSS.
// Specify the bucket name, for example, examplebucket. String bucketName = "examplebucket"; // Specify the full path of the object, for example, exampledir/exampleobject.txt. The full path of the object cannot contain the bucket name. String objectName = "exampledir/exampleobject.txt"; MultipartUploadRequest request = new MultipartUploadRequest(bucketName, objectName, fileUri); request.setProgressCallback(new OSSProgressCallback<MultipartUploadRequest>() { @Override public void onProgress(MultipartUploadRequest request, long currentSize, long totalSize) { OSSLog.logDebug("[testMultipartUpload] - " + currentSize + " " + totalSize, false); } }); OSSAsyncTask task = oss.asyncMultipartUpload(request, new OSSCompletedCallback<MultipartUploadRequest, CompleteMultipartUploadResult>() { @Override public void onSuccess(MultipartUploadRequest request, CompleteMultipartUploadResult result) { OSSLog.logInfo(result.getServerCallbackReturnBody()); } @Override public void onFailure(MultipartUploadRequest request, ClientException clientException, ServiceException serviceException) { OSSLog.logError(serviceException.getRawMessage()); } }); //Thread.sleep(100); // Cancel the multipart upload. //task.cancel(); task.waitUntilFinished();
Listar as partes enviadas
Chame o método oss.listParts para obter todas as partes enviadas de um evento de upload multipart específico.
O código a seguir mostra como listar as partes enviadas.
// Specify the bucket name, for example, examplebucket.
String bucketName = "examplebucket";
// Specify the full path of the object, for example, exampledir/exampleobject.txt. The full path of the object cannot contain the bucket name.
String objectName = "exampledir/exampleobject.txt";
// Specify the uploadId. The uploadId is obtained from the response after you call InitiateMultipartUpload to initialize the multipart upload and before you call CompleteMultipartUpload to complete the multipart upload.
String uploadId = "0004B999EF518A1FE585B0C9****";
// List parts.
ListPartsRequest listParts = new ListPartsRequest(bucketName, objectName, uploadId);
ListPartsResult result = oss.listParts(listParts);
List<PartETag> partETagList = new ArrayList<PartETag>();
for (PartSummary part : result.getParts()) {
partETagList.add(new PartETag(part.getPartNumber(), part.getETag()));
}
Se houver mais de 1.000 partes, o OSS retorna apenas as 1.000 primeiras. Na resposta, IsTruncated é false e NextPartNumberMarker indica onde começa a próxima listagem.
Cancelar um evento de upload multipart
Chame o método oss.abortMultipartUpload para cancelar um evento de upload multipart. Após o cancelamento, o uploadId correspondente não pode mais ser usado em nenhuma operação. As partes já enviadas são excluídas.
O código a seguir mostra como cancelar um evento de upload multipart.
// Specify the bucket name, for example, examplebucket.
String bucketName = "examplebucket";
// Specify the full path of the object, for example, exampledir/exampleobject.txt. The full path of the object cannot contain the bucket name.
String objectName = "exampledir/exampleobject.txt";
// Specify the uploadId. The uploadId is obtained from the response after you call InitiateMultipartUpload to initialize the multipart upload.
String uploadId = "0004B999EF518A1FE585B0C9****";
// Cancel the multipart upload.
AbortMultipartUploadRequest abort = new AbortMultipartUploadRequest(bucketName, objectName, uploadId);
AbortMultipartUploadResult abortResult = oss.abortMultipartUpload(abort);
Referências
Para ver o código de exemplo completo do upload multipart, consulte GitHub.
-
Um upload multipart envolve três operações de API:
Inicializar um upload multipart: InitiateMultipartUpload.
Enviar uma parte: UploadPart.
Concluir um upload multipart: CompleteMultipartUpload.
Para mais informações sobre a operação de API usada para cancelar um evento de upload multipart, consulte AbortMultipartUpload.
Para mais informações sobre a operação de API usada para listar as partes enviadas, consulte ListParts.
Para mais informações sobre a operação de API usada para listar todos os eventos de upload multipart em andamento (eventos iniciados, mas ainda não concluídos ou cancelados), consulte ListMultipartUploads.
Para mais informações sobre como inicializar uma instância OSSClient, consulte Initialize an OSSClient instance for Android.