阿里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 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 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 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 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 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;
	}
}

九、工具类下载路径:

传送门

你可能感兴趣的:(后端技术杂述)