package com.baidubce.services.vod; import java.io.FileInputStream; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.List; import java.util.concurrent.Callable; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.baidubce.services.bos.BosClient; import com.baidubce.services.bos.model.AbortMultipartUploadRequest; import com.baidubce.services.bos.model.CompleteMultipartUploadRequest; import com.baidubce.services.bos.model.CompleteMultipartUploadResponse; import com.baidubce.services.bos.model.InitiateMultipartUploadRequest; import com.baidubce.services.bos.model.InitiateMultipartUploadResponse; import com.baidubce.services.bos.model.PartETag; import com.baidubce.services.bos.model.UploadPartRequest; import com.baidubce.services.bos.model.UploadPartResponse; public class FileUploadSession { private static Logger logger = LoggerFactory.getLogger(FileUploadSession.class); static final long CHUNK_SIZE = 1024 * 1024 * 5L; private BosClient bosClient; private String bucket; private String bosKey; private String uploadId; private File file; private List<PartETag> partETags; public FileUploadSession(BosClient bosClient) { this.bosClient = bosClient; } public boolean upload(File file, String bucket, String bosKey) { this.file = file; this.bucket = bucket; this.bosKey = bosKey; long fileLength = file.length(); logger.info("[fileLength] = " + fileLength); int parts = (int) (fileLength / CHUNK_SIZE); if (fileLength % CHUNK_SIZE > 0) { parts++; } logger.info("[fileLength] = " + fileLength + ", [parts] = " + parts); partETags = new ArrayList<PartETag>(parts); initMultipartUpload(); ExecutorService pool = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors()); // ExecutorService pool = Executors.newFixedThreadPool(1); List<Future<Boolean>> futures = new ArrayList<Future<Boolean>>(parts); for (int i = 0; i < parts; i++) { futures.add(pool.submit(new UploadPartTask(this, i))); } boolean success = true; for (int i = 0; i < futures.size(); i++) { Future<Boolean> future = futures.get(i); try { if (future.get()) { logger.info("The upload task [ " + i + "] completed."); } else { logger.error("The upload task [ " + i + "] failed."); success = false; } } catch (Exception e) { success = false; } } pool.shutdownNow(); if (success) { Collections.sort(partETags, new Comparator<PartETag>() { public int compare(PartETag a, PartETag b) { return a.getPartNumber() - b.getPartNumber(); } }); // send multi-part upload completion request CompleteMultipartUploadRequest completeMultipartUploadRequest = new CompleteMultipartUploadRequest(bucket, bosKey, uploadId, partETags); CompleteMultipartUploadResponse response = bosClient.completeMultipartUpload(completeMultipartUploadRequest); logger.info("Success to upload file: " + file.getAbsolutePath() + " to BOS with ETag: " + response.getETag()); } else { AbortMultipartUploadRequest abortMultipartUploadRequest = new AbortMultipartUploadRequest(bucket, bosKey, uploadId); bosClient.abortMultipartUpload(abortMultipartUploadRequest); logger.info("Failed to upload file: " + file.getAbsolutePath()); } return success; } private boolean uploadPart(int partNum) { logger.info("Upload part: " + partNum); int tryCount = 5; while (tryCount > 0) { FileInputStream fis = null; try { fis = new FileInputStream(file); long skipBytes = CHUNK_SIZE * partNum; fis.skip(skipBytes); // compute chunk size long partSize = (CHUNK_SIZE < file.length() - skipBytes) ? CHUNK_SIZE : file.length() - skipBytes; logger.info("[skipBytes]= " + skipBytes + ", [partSize] = " + partSize + ", [file.length() - skipBytes] = " + (file.length() - skipBytes)); // upload chunk UploadPartRequest uploadPartRequest = new UploadPartRequest(); uploadPartRequest.setBucketName(bucket); uploadPartRequest.setKey(bosKey); uploadPartRequest.setUploadId(uploadId); uploadPartRequest.setInputStream(fis); uploadPartRequest.setPartSize(partSize); // part number is 1-based uploadPartRequest.setPartNumber(partNum + 1); UploadPartResponse uploadPartResponse = bosClient.uploadPart(uploadPartRequest); // add ETag to result list partETags.add(uploadPartResponse.getPartETag()); logger.info("Complete upload with ETag: " + uploadPartResponse.getPartETag()); } catch (IOException e) { logger.error("Failed to upload the part " + partNum + " [tryCount] = " + tryCount); tryCount--; continue; } finally { if (fis != null) { try { fis.close(); } catch (Exception e) { // ignore } } } break; } if (tryCount == 0) { logger.error("Failed to upload the part " + partNum); } else { logger.info("Success to upload the part " + partNum); } return tryCount > 0; } private void initMultipartUpload() { InitiateMultipartUploadRequest initiateMultipartUploadRequest = new InitiateMultipartUploadRequest(bucket, bosKey); InitiateMultipartUploadResponse initiateMultipartUploadResponse = bosClient.initiateMultipartUpload(initiateMultipartUploadRequest); uploadId = initiateMultipartUploadResponse.getUploadId(); logger.info("[uploadId] = " + uploadId); } class UploadPartTask implements Callable<Boolean> { int partNum; FileUploadSession session; UploadPartTask(FileUploadSession session, int partNum) { this.session = session; this.partNum = partNum; } public Boolean call() { return session.uploadPart(partNum); } } }