【OSS】OSS的Multipart上传

OSS中大文件上传建议使用

Mu
package com.ls;

import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

import com.aliyun.oss.ClientConfiguration;
import com.aliyun.oss.ClientException;
import com.aliyun.oss.OSSClient;
import com.aliyun.oss.OSSErrorCode;
import com.aliyun.oss.OSSException;
import com.aliyun.oss.ServiceException;
import com.aliyun.oss.model.AbortMultipartUploadRequest;
import com.aliyun.oss.model.CompleteMultipartUploadRequest;
import com.aliyun.oss.model.GetObjectRequest;
import com.aliyun.oss.model.InitiateMultipartUploadRequest;
import com.aliyun.oss.model.InitiateMultipartUploadResult;
import com.aliyun.oss.model.ListMultipartUploadsRequest;
import com.aliyun.oss.model.MultipartUpload;
import com.aliyun.oss.model.MultipartUploadListing;
import com.aliyun.oss.model.OSSObjectSummary;
import com.aliyun.oss.model.ObjectListing;
import com.aliyun.oss.model.PartETag;
import com.aliyun.oss.model.UploadPartRequest;
import com.aliyun.oss.model.UploadPartResult;

/**
 * 该示例代码展示了如何使用OSS的Multipart上传方式进行多线程分段上传较大文件。
 * 
 * 该示例代码执行的过程为: 
 * 1. 检查指定的Bucket是否存在,如果不存在则创建它;
 * 2. 根据文件的大小计算应该将文件分成多少个Part进行上传; 
 * 3.初始化Multipart上传请求; 
 * 4. 使用ExecutorService并发在上传每个Part; 
 * 5.如果所有Part均上传成功,则完成这个上传请求; 
 * 6.最后清理掉测试资源:删除上传的Object或未完成的Multipart上传请求,并删除这个Bucket。
 * 
 * 尝试运行这段示例代码时需要注意: 
 * 1.为了展示在删除Bucket时除了需要删除其中的Objects,也需要取消掉未完成的Multipartuploads, 示例代码最后为删除掉指定的Bucket,因为不要使用您的已经有资源的Bucket进行测试! 
 * 2.请使用您的API授权密钥填充ACCESS_ID和ACCESS_KEY常量; 
 * 3.需要准确上传用的测试文件,该文件大小要大于一个Part的最小字节数5MB,但不能大于 一个Object允许的最大字节数5GB。
 * 并修改常量UPLOAD_FILE_PATH为测试文件的路径;修改常量DOWNLOAD_FILE_PATH为下载文件的路径。 
 * 4.该程序仅为示例代码,仅供参考,并不能保证足够健壮。
 **/
public class OSSMultipartSample {

	private static final String ACCESS_ID = "******************";
	private static final String ACCESS_KEY = "*************************";
	private static final String OSS_ENDPOINT = "http://oss.aliyuncs.com/";

	private static final String UPLOAD_FILE_PATH = "G:/test/Maven实战.pdf";
	private static final String DOWNLOAD_FILE_PATH = "G:/test/download/Maven实战.pdf";

	private static final long PART_SIZE = 5 * 1024 * 1024L; // 每个Part的大小,最小为5MB
	private static final int CONCURRENCIES = 2; // 上传Part的并发线程数。

	public static void main(String[] args) throws Exception {
		// 可以使用ClientConfiguration对象设置代理服务器、最大重试次数等参数。
		ClientConfiguration config = new ClientConfiguration();
		OSSClient client = new OSSClient(OSS_ENDPOINT, ACCESS_ID, ACCESS_KEY,
				config);

		String bucketName = ACCESS_ID.toLowerCase() + "-multipart-test";

		System.out.println("ACCESS_ID.toLowerCase():" + ACCESS_ID.toLowerCase());

		String key = "Maven实战.pdf";

		File uploadFile = new File(UPLOAD_FILE_PATH);
		if (!uploadFile.exists())
			System.err.println("无法找到文件:" + UPLOAD_FILE_PATH);

		// 准备Bucket
		ensureBucket(client, bucketName);

		try {

			// 使用multipart的方式上传文件
			System.out.println("正在上传...");
			long startTime = System.currentTimeMillis();
			uploadBigFile(client, bucketName, key, uploadFile);
			long endTime = System.currentTimeMillis();
			System.out.println("上传花费时间约:" + (endTime - startTime) + " ms");

			// 下载上传完成的Object
			System.out.println("正在下载...");
			long startTime_d = System.currentTimeMillis();
			downloadFile(client, bucketName, key, DOWNLOAD_FILE_PATH);
			long endTime_d = System.currentTimeMillis();
			System.out.println("下载花费时间约:" + (endTime_d - startTime_d) + " ms");
		} finally {
			// 清理测试资源
			deleteBucket(client, bucketName);
		}
	}

	// 创建Bucket
	private static void ensureBucket(OSSClient client, String bucketName)
			throws OSSException, ClientException {

		try {
			// 创建bucket
			client.createBucket(bucketName);
		} catch (ServiceException e) {
			if (!OSSErrorCode.BUCKET_ALREADY_EXISTS.equals(e.getErrorCode())) {
				// 如果Bucket已经存在,则忽略
				throw e;
			}
		}
	}

	// 删除掉Bucket
	private static void deleteBucket(OSSClient client, String bucketName)
			throws OSSException, ClientException {

		// 删除bucket之前必须保证bucket为空,所以先必须先删除object和multipart

		// 如果存在,查看bucket是否为空
		ObjectListing ObjectListing = client.listObjects(bucketName);
		List<OSSObjectSummary> listDeletes = ObjectListing.getObjectSummaries();
		for (int i = 0; i < listDeletes.size(); i++) {
			String objectName = listDeletes.get(i).getKey();
			// 如果不为空,先删除bucket下的文件
			client.deleteObject(bucketName, objectName);
		}

		// 删除所有未完成的multipart uploads.
		ListMultipartUploadsRequest listMultipartUploadsRequest = new ListMultipartUploadsRequest(
				bucketName);
		MultipartUploadListing uploadListing = client
				.listMultipartUploads(listMultipartUploadsRequest);

		for (MultipartUpload upload : uploadListing.getMultipartUploads()) {
			String key = upload.getKey();
			AbortMultipartUploadRequest abortMultipartUploadRequest = new AbortMultipartUploadRequest(
					bucketName, key, upload.getUploadId());

			client.abortMultipartUpload(abortMultipartUploadRequest);
		}

		// 删除bucket
		client.deleteBucket(bucketName);
	}

	// 通过Multipart的方式上传一个大文件
	// 要上传文件的大小必须大于一个Part允许的最小大小,即5MB。
	private static void uploadBigFile(OSSClient client, String bucketName,
			String key, File uploadFile) throws OSSException, ClientException,
			InterruptedException {

		int partCount = calPartCount(uploadFile);
		if (partCount <= 1) {
			throw new IllegalArgumentException("要上传文件的大小必须大于一个Part的字节数:"
					+ PART_SIZE);
		}

		String uploadId = initMultipartUpload(client, bucketName, key);

		ExecutorService pool = Executors.newFixedThreadPool(CONCURRENCIES);

		List<PartETag> eTags = Collections
				.synchronizedList(new ArrayList<PartETag>());

		for (int i = 0; i < partCount; i++) {
			System.out.println("正在上传第" + i + "部分");
			long start = PART_SIZE * i;
			long curPartSize = PART_SIZE < uploadFile.length() - start ? PART_SIZE
					: uploadFile.length() - start;

			pool.execute(new UploadPartThread(client, bucketName, key,
					uploadFile, uploadId, i + 1, PART_SIZE * i, curPartSize,
					eTags));
		}

		pool.shutdown();
		while (!pool.isTerminated()) {
			pool.awaitTermination(5, TimeUnit.SECONDS);
		}

		if (eTags.size() != partCount) {
			throw new IllegalStateException("Multipart上传失败,有Part未上传成功。");
		}

		completeMultipartUpload(client, bucketName, key, uploadId, eTags);
	}

	// 根据文件的大小和每个Part的大小计算需要划分的Part个数。
	private static int calPartCount(File f) {
		int partCount = (int) (f.length() / PART_SIZE);
		if (f.length() % PART_SIZE != 0) {
			partCount++;
		}
		return partCount;
	}

	// 初始化一个Multi-part upload请求。
	private static String initMultipartUpload(OSSClient client,
			String bucketName, String key) throws OSSException, ClientException {
		InitiateMultipartUploadRequest initUploadRequest = new InitiateMultipartUploadRequest(
				bucketName, key);
		InitiateMultipartUploadResult initResult = client
				.initiateMultipartUpload(initUploadRequest);
		String uploadId = initResult.getUploadId();
		return uploadId;
	}

	// 完成一个multi-part请求。
	private static void completeMultipartUpload(OSSClient client,
			String bucketName, String key, String uploadId, List<PartETag> eTags)
			throws OSSException, ClientException {
		// 为part按partnumber排序
		Collections.sort(eTags, new Comparator<PartETag>() {

			public int compare(PartETag arg0, PartETag arg1) {
				PartETag part1 = arg0;
				PartETag part2 = arg1;

				return part1.getPartNumber() - part2.getPartNumber();
			}
		});

		CompleteMultipartUploadRequest completeMultipartUploadRequest = new CompleteMultipartUploadRequest(
				bucketName, key, uploadId, eTags);

		client.completeMultipartUpload(completeMultipartUploadRequest);
	}

	private static class UploadPartThread implements Runnable {
		private File uploadFile;
		private String bucket;
		private String object;
		private long start;
		private long size;
		private List<PartETag> eTags;
		private int partId;
		private OSSClient client;
		private String uploadId;

		UploadPartThread(OSSClient client, String bucket, String object,
				File uploadFile, String uploadId, int partId, long start,
				long partSize, List<PartETag> eTags) {
			this.uploadFile = uploadFile;
			this.bucket = bucket;
			this.object = object;
			this.start = start;
			this.size = partSize;
			this.eTags = eTags;
			this.partId = partId;
			this.client = client;
			this.uploadId = uploadId;
		}

		@Override
		public void run() {

			InputStream in = null;
			try {
				in = new FileInputStream(uploadFile);
				in.skip(start);

				UploadPartRequest uploadPartRequest = new UploadPartRequest();
				uploadPartRequest.setBucketName(bucket);
				uploadPartRequest.setKey(object);
				uploadPartRequest.setUploadId(uploadId);
				uploadPartRequest.setInputStream(in);
				uploadPartRequest.setPartSize(size);
				uploadPartRequest.setPartNumber(partId);

				UploadPartResult uploadPartResult = client
						.uploadPart(uploadPartRequest);

				eTags.add(uploadPartResult.getPartETag());

			} catch (Exception e) {
				e.printStackTrace();
			} finally {
				if (in != null)
					try {
						in.close();
					} catch (Exception e) {
					}
			}
		}
	}

	// 下载Object到本地文件。
	private static void downloadFile(OSSClient client, String bucketName,
			String key, String downloadFilePath) throws OSSException,
			ClientException {
		client.getObject(new GetObjectRequest(bucketName, key), new File(
				downloadFilePath));
	}
}

ltipart上传方式。
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,以下是使用 Spring Boot 和阿里云 OSS Java SDK 实现断点上传并显示上传进度的示例代码: ```java import com.aliyun.oss.*; import com.aliyun.oss.model.UploadFileRequest; import com.aliyun.oss.model.UploadFileResult; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.web.multipart.MultipartFile; @Service public class OSSService { // 阿里云 OSS 客户端 private final OSS ossClient; // 构造函数注入 OSS 客户端对象 @Autowired public OSSService(OSS ossClient) { this.ossClient = ossClient; } /** * 断点上传文件到 OSS * * @param file 要上传的文件 * @param bucket 存储空间名称 * @param object 存储对象名称 * @param progress 上传进度回调函数 * @throws IOException */ public void uploadFileToOSS(MultipartFile file, String bucket, String object, ProgressCallback progress) throws IOException { // 创建 UploadFileRequest 对象 UploadFileRequest uploadFileRequest = new UploadFileRequest(bucket, object); // 设置要上传的文件 File localFile = File.createTempFile("temp", null); file.transferTo(localFile); uploadFileRequest.setUploadFile(localFile.getAbsolutePath()); // 创建 ResumableUploader 对象 ResumableUploader uploader = new ResumableUploader(ossClient, uploadFileRequest); // 设置上传进度回调函数 uploader.setProgressCallback(progress); // 开始上传文件 UploadFileResult result = uploader.upload(); // 删除本地临时文件 localFile.delete(); // 打印上传结果 System.out.println("ETag: " + result.getETag()); System.out.println("RequestId: " + result.getRequestId()); } } ``` 在上述代码中,我们定义了一个名为 `uploadFileToOSS` 的方法,该方法接收要上传的文件、存储空间名称、存储对象名称和上传进度回调函数作为参数。在方法内部,我们首先创建 `UploadFileRequest` 对象,并设置要上传的文件。然后,我们创建 `ResumableUploader` 对象,并设置上传进度回调函数。最后,我们调用 `upload` 方法开始上传文件。 上传进度回调函数的实现如下: ```java import com.aliyun.oss.event.ProgressEvent; import com.aliyun.oss.event.ProgressEventType; import com.aliyun.oss.event.ProgressListener; public class OSSProgressCallback implements ProgressListener { private final long totalSize; public OSSProgressCallback(long totalSize) { this.totalSize = totalSize; } @Override public void progressChanged(ProgressEvent event) { if (event.getEventType() == ProgressEventType.REQUEST_BYTE_TRANSFER_EVENT) { long bytesWritten = event.getBytes(); System.out.println("Uploaded " + bytesWritten + " bytes of " + totalSize + " bytes"); } } } ``` 在上述代码中,我们定义了一个名为 `OSSProgressCallback` 的类,该类实现了 `ProgressListener` 接口。在 `progressChanged` 方法中,我们判断事件类型是否为 `REQUEST_BYTE_TRANSFER_EVENT`,如果是,则表示已上传的字节数发生了变化,我们就可以计算出当前上传进度并输出。 在 Spring Boot 控制器中调用 `uploadFileToOSS` 方法,并传入上传进度回调函数: ```java import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; import org.springframework.web.multipart.MultipartFile; @RestController public class OSSController { private final OSSService ossService; @Autowired public OSSController(OSSService ossService) { this.ossService = ossService; } @PostMapping("/upload") public String uploadFile(@RequestParam("file") MultipartFile file) throws IOException { String bucketName = "<yourBucketName>"; String objectName = "<yourObjectName>"; // 计算文件大小 long fileSize = file.getSize(); // 创建上传进度回调函数 ProgressCallback progressCallback = new OSSProgressCallback(fileSize); // 上传文件到 OSS ossService.uploadFileToOSS(file, bucketName, objectName, progressCallback); return "File uploaded successfully!"; } } ``` 需要注意的是,这里需要先注入一个阿里云 OSS 客户端对象,示例代码中使用了构造函数注入的方式。另外,需要替换代码中的 `bucketName` 和 `objectName` 参数为自己阿里云账号的信息。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值