1. 程式人生 > >阿里Oss物件儲存服務基本工具整合

阿里Oss物件儲存服務基本工具整合

一、引言

阿里的oss物件儲存伺服器,提供了儲存空間管理,檔案上傳下載,檔案管理,音訊與影象處理等常用操作,基本滿足中小企業對於檔案處理的需求,但官方提供的Api介面都是簡版,demo級別的,並不適合直接使用,故在下在工作之餘,對其常用Api進行封裝,整合為OssUtils工具類。

二、Oss基本配置

阿里官網推舉用子access_key_id和access_key_secrt,而不採用全域性配置

/**
 * 物件儲存Oss工具類
 *
 * @author sunyiran
 * @date 2018-11-07
 */
@Slf4j
public class OssUtils {

	/**
	 * 連線URL
	 */
	private static final String ENDPOINT = "XXXX";
	/**
	 * 例項ID
	 */
	private static final String ACCESS_KEY_ID = "XXXX";
	/**
	 * 例項金鑰
	 */
	private static final String ACCESS_KEY_SECRET = "XXXX";
	/**
	 * 預設儲存空間
	 */
	private static final String DEFAULT_BUCKETNAME = "syr-test";
	/**
	 * 預設圖片格式
	 */
	private static final String DEFAULT_IMAGE_TYPE = "png";
	/**
	 * 預設壓縮比例
	 */
	private static final int DEFAULT_SCALE = 10;
	/**
	 * 預設水印文字
	 */
	private static final String DEFAULT_CONTENT = "我的水印";
	/**
	 * 連線例項
	 */
	private static final OSSClient OSS_CLIENT;

	private static final String SUCCESS = "success";
	private static final String FAILED = "failed";

	static {
		// 建立ClientConfiguration例項,按照您的需要修改預設引數。
		ClientConfiguration conf = new ClientConfiguration();
		// 關閉CNAME選項。
		conf.setSupportCname(false);
		OSS_CLIENT = new OSSClient(ENDPOINT, ACCESS_KEY_ID, ACCESS_KEY_SECRET, conf);
	}
}

三、管理儲存空間

採用不同的儲存空間可以講檔案分類別儲存,它具有建立,列舉,獲取詳情,刪除等操作。

 	/**
	 * 儲存空間管理工具
	 */
	public static class BucketUtils {
		/**
		 * 建立新的儲存空間
		 *
		 * @param bucketName
		 */
		public static String createBucket(String bucketName) {
			CreateBucketRequest createBucketRequest = new CreateBucketRequest(bucketName);
			// 設定儲存空間的許可權為公共讀,預設是私有。
			createBucketRequest.setCannedACL(CannedAccessControlList.PublicRead);
			// 設定儲存空間的儲存型別為低頻訪問型別,預設是標準型別。
			createBucketRequest.setStorageClass(StorageClass.IA);
			try {
				OSS_CLIENT.createBucket(createBucketRequest);
				return SUCCESS;
			} catch (OSSException e) {
				log.error(e.getMessage());
				return FAILED;
			}
		}

		/**
		 * 列舉儲存空間
		 *
		 * @param bucketPrefix
		 */
		public static List<Bucket> listBucket(String bucketPrefix) {
			ListBucketsRequest listBucketsRequest = new ListBucketsRequest();
			// 列舉指定字首的儲存空間。
			if (StringUtils.isNotBlank(bucketPrefix)) {
				listBucketsRequest.setPrefix(bucketPrefix);
			}
			try {
				return OSS_CLIENT.listBuckets(listBucketsRequest).getBucketList();
			} catch (OSSException e) {
				log.error(e.getMessage());
				return null;
			}
		}

		/**
		 * 獲取儲存空間資訊
		 *
		 * @param bucketName
		 */
		public static Map getBucketInfo(String bucketName) {
			//判斷空間是否存在
			boolean exists = OSS_CLIENT.doesBucketExist(bucketName);
			if (!exists) {
				return null;
			}
			// 儲存空間的資訊包括地域(Region或Location)、建立日期(CreationDate)、擁有者(Owner)、許可權(Grants)等。
			Map<String, Object> result = new HashMap<>(4);
			BucketInfo info = OSS_CLIENT.getBucketInfo(bucketName);
			// 獲取地域。
			result.put("location", info.getBucket().getLocation());
			// 獲取建立日期。
			result.put("createTime", new SimpleDateFormat("yyyy-MM-dd HH:ss").format(info.getBucket().getCreationDate()));
			// 獲取擁有者資訊。
			result.put("owner", info.getBucket().getOwner());
			// 獲取許可權資訊。';;

			AccessControlList acl = OSS_CLIENT.getBucketAcl(bucketName);
			result.put("authority", acl.toString());

			return result;
		}

		/**
		 * 刪除儲存空間
		 *
		 * @param bucketName
		 */
		public static boolean deleteBucket(String bucketName) {
			//判斷空間是否存在
			boolean exists = OSS_CLIENT.doesBucketExist(bucketName);
			if (!exists) {
				return false;
			}
			// 刪除儲存空間。
			OSS_CLIENT.deleteBucket(bucketName);
			return true;
		}
	}

四、檔案上傳

阿里提供了流傳輸,分片傳輸,斷點續傳,帶進度條的等上傳操作

	/**
	 * 檔案上傳工具
	 */
	public static class UploadUtils {

		/**
		 * 預設上傳
		 *
		 * @param file
		 * @return
		 * @throws IOException
		 */
		public static String simpleUpload(MultipartFile file) {
			try {
				return upload(file, DEFAULT_BUCKETNAME);
			} catch (IOException e) {
				e.printStackTrace();
			}
			return null;
		}

		/**
		 * 自定義儲存空間上傳配置
		 *
		 * @param file
		 * @return
		 * @throws IOException
		 */
		public static String upload(MultipartFile file, String bucketName) throws IOException {
			String putName = file.getOriginalFilename() + "_" + UUID.randomUUID().toString();
			// 上傳檔案流。
			PutObjectResult putObject = OSS_CLIENT.putObject(bucketName, putName, file.getInputStream());
			String errorResponseAsString = putObject.getResponse().getErrorResponseAsString();
			if (StringUtils.isNotBlank(errorResponseAsString)) {
				return errorResponseAsString;
			}
			System.out.println(errorResponseAsString);
			return putName;
		}


		/**
		 * 合併檔案(主要用於文字物件)
		 *
		 * @param bucketName
		 * @param contentType
		 * @param content
		 * @return
		 */
		public static String mergeContent(String bucketName, String contentType, InputStream... content) {
			ObjectMetadata meta = new ObjectMetadata();
			// 指定上傳的內容型別。
			meta.setContentType(contentType);
			//設定檔名
			String fileName = UUID.randomUUID().toString();
			AppendObjectRequest appendObjectRequest = new AppendObjectRequest(bucketName, fileName, content[0], meta);
			AppendObjectResult objectResult = null;
			try {
				for (int i = 0; i < content.length; i++) {
					if (i == 0) {
						appendObjectRequest.setPosition(0L);
						objectResult = OSS_CLIENT.appendObject(appendObjectRequest);
					} else {
						appendObjectRequest.setPosition(objectResult.getNextPosition());
						appendObjectRequest.setInputStream(content[i]);
						OSS_CLIENT.appendObject(appendObjectRequest);
					}
				}
				return fileName;
			} catch (OSSException e) {
				log.error(e.getMessage());
				return FAILED;
			}

		}

		/**
		 * 分片上傳
		 *
		 * @param bucketName
		 * @param file
		 * @return
		 * @throws IOException
		 */
		public static String shardUpload(String bucketName, MultipartFile file) throws IOException {
			//設定檔名
			String putName = file.getOriginalFilename() + "_" + UUID.randomUUID().toString();
			/* 步驟1:初始化一個分片上傳事件。
			 */
			InitiateMultipartUploadRequest request = new InitiateMultipartUploadRequest(bucketName, putName);
			InitiateMultipartUploadResult result = OSS_CLIENT.initiateMultipartUpload(request);
			// 返回uploadId,它是分片上傳事件的唯一標識,您可以根據這個ID來發起相關的操作,如取消分片上傳、查詢分片上傳等。
			String uploadId = result.getUploadId();

			/* 步驟2:上傳分片。
			 */
			// partETags是PartETag的集合。PartETag由分片的ETag和分片號組成。
			List<PartETag> partETags = new ArrayList<>();
			// 計算檔案有多少個分片。
			final long partSize = 1024 * 1024L;
			long fileLength = file.getSize();
			int partCount = (int) (fileLength / partSize);
			if (fileLength % partSize != 0) {
				partCount++;
			}
			// 遍歷分片上傳。
			for (int i = 0; i < partCount; i++) {
				long startPos = i * partSize;
				long curPartSize = (i + 1 == partCount) ? (fileLength - startPos) : partSize;
				InputStream instep = file.getInputStream();
				// 跳過已經上傳的分片。
				instep.skip(startPos);
				UploadPartRequest uploadPartRequest = new UploadPartRequest();
				uploadPartRequest.setBucketName(bucketName);
				uploadPartRequest.setKey(putName);
				uploadPartRequest.setUploadId(uploadId);
				uploadPartRequest.setInputStream(instep);
				// 設定分片大小。除了最後一個分片沒有大小限制,其他的分片最小為100KB。
				uploadPartRequest.setPartSize(curPartSize);
				// 設定分片號。每一個上傳的分片都有一個分片號,取值範圍是1~10000,如果超出這個範圍,OSS將返回InvalidArgument的錯誤碼。
				uploadPartRequest.setPartNumber(i + 1);
				// 每個分片不需要按順序上傳,甚至可以在不同客戶端上傳,OSS會按照分片號排序組成完整的檔案。
				UploadPartResult uploadPartResult = OSS_CLIENT.uploadPart(uploadPartRequest);
				// 每次上傳分片之後,OSS的返回結果會包含一個PartETag。PartETag將被儲存到partETags中。
				partETags.add(uploadPartResult.getPartETag());
			}

			/* 步驟3:完成分片上傳。
			 */
			// 排序。partETags必須按分片號升序排列。
			partETags.sort(Comparator.comparingInt(PartETag::getPartNumber));
			// 在執行該操作時,需要提供所有有效的partETags。OSS收到提交的partETags後,會逐一驗證每個分片的有效性。當所有的資料分片驗證通過後,OSS將把這些分片組合成一個完整的檔案。
			CompleteMultipartUploadRequest completeMultipartUploadRequest =
				new CompleteMultipartUploadRequest(bucketName, putName, uploadId, partETags);
			OSS_CLIENT.completeMultipartUpload(completeMultipartUploadRequest);
			return putName;
		}

		/**
		 * 帶進度條上傳
		 *
		 * @param bucketName
		 * @param file
		 * @return
		 */
		public static String uploadWithProgress(String bucketName, MultipartFile file) throws IOException {
			String putName = file.getOriginalFilename() + "_" + UUID.randomUUID().toString();
			try {
				OSS_CLIENT.putObject(new PutObjectRequest(bucketName, putName, file.getInputStream()).withProgressListener(new PutObjectProgressListener()));
				return putName;
			} catch (OSSException e) {
				log.error(e.getMessage());
				return FAILED;
			}

		}
	}

五、檔案下載

	/**
	 * 檔案下載工具類
	 */
	public static class DownloadUtils {

		/**
		 * 預設下載
		 *
		 * @param fileUrl
		 * @return
		 */
		public static byte[] simpleDownload(String fileUrl) {
			try {
				download(DEFAULT_BUCKETNAME, fileUrl);
			} catch (IOException e) {
				e.printStackTrace();
			}
			return null;
		}

		public static byte[] download(String bucketName, String fileUrl) throws IOException {

			// ossObject包含檔案所在的儲存空間名稱、檔名稱、檔案元資訊以及一個輸入流。
			OSSObject ossObject = OSS_CLIENT.getObject(bucketName, fileUrl);

			return ossObjectToByte(ossObject);
		}
	}

六、檔案管理

	/**
	 * 檔案管理
	 */
	public static class FileManager {

		/**
		 * 判斷檔案是否存在
		 *
		 * @param bucketName
		 * @param fileUrl
		 * @return
		 */
		public static boolean isExist(String bucketName, String fileUrl) {
			// 判斷檔案是否存在。
			return OSS_CLIENT.doesObjectExist(bucketName, fileUrl);
		}

		/**
		 * 獲取檔案資訊
		 *
		 * @param bucketName
		 * @param fileUrl
		 * @return
		 */
		public static Map getFileInfo(String bucketName, String fileUrl) {
			// 獲取檔案元資訊。
			ObjectMetadata metadata = OSS_CLIENT.getObjectMetadata(bucketName, fileUrl);
			return metadata.getRawMetadata();
		}

		/**
		 * 獲取指定字首的檔案
		 *
		 * @param bucketName
		 * @param filePrefix
		 * @return
		 */
		public static List<OSSObjectSummary> listFile(String bucketName, String filePrefix) {
			// 列舉包含指定字首的檔案。預設列舉100個檔案。
			ObjectListing objectListing = OSS_CLIENT.listObjects(new ListObjectsRequest(bucketName).withPrefix(filePrefix));
			return objectListing.getObjectSummaries();
		}

		/**
		 * 刪除檔案
		 *
		 * @param bucketName
		 * @param fileUrl
		 * @return
		 */
		public static String delete(String bucketName, String fileUrl) {
			// 刪除檔案。
			try {
				OSS_CLIENT.deleteObject(bucketName, fileUrl);
				return SUCCESS;
			} catch (OSSException e) {
				log.error(e.getMessage());
				return FAILED;
			}
		}

	}

七、圖片處理

	/**
	 * 圖片處理工具類
	 */
	public static class ImageUtils {
		public static void test() {
//			// 縮放
//			String style = "image/resize,m_fixed,w_100,h_100";
//			GetObjectRequest request = new GetObjectRequest(bucketName, objectName);
//			request.setProcess(style);
//			ossClient.getObject(request, new File("example-resize.jpg"));
//// 水印
//			style = "image/watermark,text_SGVsbG8g5Zu-54mH5pyN5YqhIQ";
//			request = new GetObjectRequest(bucketName, objectName);
//			request.setProcess(style);
//			ossClient.getObject(request, new File("example-watermark.jpg"));
//// 格式轉換
//			style = "image/format,png";
//			request = new GetObjectRequest(bucketName, objectName);
//			request.setProcess(style);
//			ossClient.getObject(request, new File("example-format.png"));
//// 獲取圖片資訊
//			style = "image/info";
//			request = new GetObjectRequest(bucketName, objectName);
//			request.setProcess(style);
//			ossClient.getObject(request, new File("example-info.txt"));
//// 關閉OSSClient。
//			ossClient.shutdown();
		}

		/**
		 * 圖片等比例縮放
		 *
		 * @param scale
		 * @param bucketName
		 * @param fileUrl
		 * @return
		 */
		public static byte[] resize(int scale, String bucketName, String fileUrl) {
			// 縮放
			String style = "image/resize,p_" + scale;
			GetObjectRequest request = new GetObjectRequest(bucketName, fileUrl);
			request.setProcess(style);
			OSSObject object = OSS_CLIENT.getObject(request);
			return ossObjectToByte(object);
		}


		/**
		 * 新增水印
		 *
		 * @param content
		 * @param bucketName
		 * @param fileUrl
		 * @return
		 */
		public static byte[] waterMark(String content, String bucketName, String fileUrl) {
			// 水印
			String style = null;
			try {
				style = "image/watermark,text_" + Base64.getEncoder().encodeToString(content.getBytes("utf8"));
			} catch (UnsupportedEncodingException e) {
				e.printStackTrace();
			}
			GetObjectRequest request = new GetObjectRequest(bucketName, fileUrl);
			request.setProcess(style);
			OSSObject object = OSS_CLIENT.getObject(request);
			return ossObjectToByte(object);
		}
	}

附本地圖片處理方案:

    /**
     * 等比例壓縮圖片
     *
     * @param scale      縮放比例
     * @param targetPath 被處理圖片路徑
     * @param path       處理後圖片存放路徑
     * @return
     * @throws IOException
     */
    public static File changeImgSize(double scale, String targetPath, String path) throws IOException {

        BufferedImage img = ImageIO.read(getInputStream(targetPath));
        int w = (int) (img.getWidth() * scale);
        int h = (int) (img.getHeight() * scale);
        BufferedImage image = new BufferedImage(w, h, BufferedImage.TYPE_USHORT_555_RGB);
        image.getGraphics().drawImage(img, 0, 0, w, h, null);
        File destFile = new File(getPath(path));

        FileOutputStream out = new FileOutputStream(destFile);
        // 可以正常實現bmp、png、gif轉jpg
        JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(out);
        encoder.encode(image);
        out.close();
        return destFile;
    }

八、使用用例

/**
 * @author sunyiran
 * @date 2018-11-07
 */
@RestController("/file")
public class FileController {

	/**
	 * 檔案下載
	 * @param fileUrl
	 * @return
	 */
	@PostMapping("/download")
	public static ResponseEntity<byte[]> download(String fileUrl) {
		HttpHeaders headers = new HttpHeaders();
		headers.add("Cache-Control", "no-cache, no-store, must-revalidate");
		headers.add("Content-Disposition", String.format("attachment; filename=\"%s\"", "test.jpg"));
		headers.add("Pragma", "no-cache");
		headers.add("Expires", "0");
		byte[] download = OssUtils.DownloadUtils.simpleDownload(fileUrl);

		if (download == null) {
			return null;
		}
		return ResponseEntity.ok().headers(headers)
			.contentLength(download.length).contentType(MediaType.parseMediaType("application/octet-stream"))
			.body(download);
	}

	/**
	 * 上傳檔案
	 *
	 * @param file
	 * @return
	 */
	@PostMapping("/upload")
	public static String upload(MultipartFile file) {
		String fileUrl = OssUtils.UploadUtils.simpleUpload(file);
		return fileUrl;
	}
}

九、工具類下載路徑:

傳送門