SpringBoot三十七:整合阿里云OSS

什么是对象存储OSS

阿里云对象存储OSS(Object Storage Service)是阿里云提供的海量、安全、低成本、高可靠的云存储服务。其数据设计持久性不低于99.9999999999%(12个9),服务可用性(或业务连续性)不低于99.995%。

OSS具有与平台无关的RESTful API接口,您可以在任何应用、任何时间、任何地点存储和访问任意类型的数据。

您可以使用阿里云提供的API、SDK接口或者OSS迁移工具轻松地将海量数据移入或移出阿里云OSS。数据存储到阿里云OSS以后,您可以选择标准存储(Standard)作为移动应用、大型网站、图片分享或热点音视频的主要存储方式,也可以选择成本更低、存储期限更长的低频访问存储(Infrequent Access)、归档存储(Archive)作为不经常访问数据的存储方式。

官方文档

集成OSS

pom.xml

<dependency>
	<groupId>com.aliyun.oss</groupId>
	<artifactId>aliyun-sdk-oss</artifactId>
	<version>3.10.2</version>
</dependency>
<dependency>
	<groupId>org.projectlombok</groupId>
	<artifactId>lombok</artifactId>
</dependency>

application.yml

oss:
  endpoint: https://oss-cn-hangzhou.aliyuncs.com #Endpoint以杭州为例,其它Region请按实际情况填写
  accessKeyId: yourAccessKeyId
  secretAccessKey: yourAccessKeySecret
  bucketName: yourBucketName
  urlPrefix: 你的文件访问地址 #bucketName+endpoint

OSS客户端配置类,并注入到Spring中

@Data
@Configuration
@ConfigurationProperties(prefix = "oss")
//@PropertySource(value = {"classpath:application-oss.properties"})
public class OssConfig {

	private String endpoint;
	private String accessKeyId;
	private String secretAccessKey;
	private String bucketName;
	private String urlPrefix;

	@Bean
	public OSS oSSClient() {
		return new OSSClientBuilder().build(endpoint, accessKeyId, secretAccessKey);
	}

}

文件上传、删除、下载、图像压缩、图像添加水印

测试

@RestController
public class OssController {

	@Autowired
	private OssUtil ossUtil;

	/**
	 * 文件上传到oss
	 * 
	 * @param files
	 * @return
	 */
	@PostMapping("/oss/upload")
	public String upload(@RequestParam("file") MultipartFile file) {
		return ossUtil.putObject(file);
	}

	/**
	 * 删除文件
	 * 
	 * @param fileName
	 * @return
	 */
	@GetMapping("oss/delete")
	public String delete(@RequestParam("fileName") String fileName) {
		Boolean flag = ossUtil.deleteObject(fileName);
		if (flag) {
			return "删除成功";
		}
		return "删除失败";
	}

	/**
	 * 查询文件列表
	 * 
	 * @return
	 */
	@GetMapping("oss/list")
	public List<OSSObjectSummary> list() {
		List<OSSObjectSummary> fileList = ossUtil.listObjects();
		return fileList;
	}

	/**
	 * 下载文件
	 * 
	 * @param fileName
	 * @param response
	 * @throws IOException
	 */
	@GetMapping("oss/download")
	public void download(@RequestParam("fileName") String fileName, HttpServletResponse response) throws IOException {
		// 通知浏览器以附件形式下载
		response.setHeader("Content-Disposition",
				"attachment;filename=" + new String(fileName.getBytes(), "ISO-8859-1"));
		ossUtil.download(response.getOutputStream(), fileName);
	}

	/**
	 * 上传图片
	 * 
	 * @param file
	 * @return
	 */
	@PostMapping("/oss/uploadPic")
	public String uploadPic(@RequestParam("file") MultipartFile file) {
		boolean flag = ossUtil.isImage(file);
		if (flag) {
			return ossUtil.uploadPic(file);
		}
		return "上传失败";
	}

}

OSS工具类

/**
 * 阿里云OSS服务器工具类
 *
 */
@Component
public class OssUtil {

	@Autowired
	private OSS ossClient;

	@Autowired
	private OssConfig ossConfig;

	private static final long EXPIRATION = 3600l * 1000 * 24 * 7;// 七天

	// 允许上传的格式
	private static final String[] IMAGE_TYPE = new String[] { ".bmp", ".jpg", ".jpeg", ".gif", ".png" };

	private String PATH = "javakf/";

	/**
	 * 单文件上传
	 * 
	 * @param file
	 * @return
	 */
	public String uploadFile(MultipartFile file) {
		String fileUrl = putObject(file);
		return fileUrl;
	}

	/**
	 * 多文件上传
	 * 
	 * @param fileList
	 * @return
	 */
	public String multiUpload(List<MultipartFile> fileList) {
		String url = null;
		String fileUrl = null;
		for (int i = 0; i < fileList.size(); i++) {
			url = putObject(fileList.get(i));
			if (i == 0) {
				fileUrl = url;
			} else {
				fileUrl += ";" + url;
			}
		}
		return fileUrl.trim();
	}

	/**
	 * 上传文件-支持单文件,多文件 -返回文件访问路径,多文件以分号‘;’分隔
	 * 
	 * @param muFiles
	 * @return
	 */
	public String uploadFiles(MultipartFile... file) {
		if (file.length < 1) {
			throw new RuntimeException("上传文件为空!");
		}
		StringBuilder str = new StringBuilder();
		for (MultipartFile muFile : file) {
			str.append(putObject(muFile));
			str.append(";");
		}
		return str.deleteCharAt(str.length() - 1).toString();

	}

	/**
	 * 上传文件
	 * 
	 * @param file
	 * @return
	 */
	public String putObject(MultipartFile file) {
		try {
			String fileName = getFilePathName(file, true);
			// 上传到阿里云
			ossClient.putObject(ossConfig.getBucketName(), fileName, new ByteArrayInputStream(file.getBytes()));
			return this.ossConfig.getUrlPrefix() + fileName;
		} catch (Exception e) {
			e.printStackTrace();
			throw new RuntimeException("上传文件失败!");
		}
	}

	/**
	 * 上传图片
	 * 
	 * @param file
	 * @return
	 */
	public String uploadPic(MultipartFile file) {
		try {
			String fileName = getFilePathName(file, true);
			InputStream inputStream = file.getInputStream();
			// 如果是图片文件就进行压缩并添加水印
			if (ImageUtil.isImage(file.getOriginalFilename())) {
				inputStream = ImageUtil.getInputStream(
						ImageUtil.setWatermark(ImageUtil.compress(ImageIO.read(inputStream))),
						ImageUtil.getFileExtention(file.getOriginalFilename()));
			}
			// 上传到阿里云
			ossClient.putObject(ossConfig.getBucketName(), fileName, inputStream);
			return this.ossConfig.getUrlPrefix() + fileName;
		} catch (Exception e) {
			e.printStackTrace();
			throw new RuntimeException("上传文件失败!");
		}
	}

	/**
	 * 校验图片格式
	 * 
	 * @param file
	 * @return
	 */
	public boolean isImage(MultipartFile file) {
		boolean flag = false;
		for (String type : IMAGE_TYPE) {
			if (StringUtils.endsWithIgnoreCase(file.getOriginalFilename(), type)) {
				flag = true;
				break;
			}
		}
		return flag;
	}

	/**
	 * 删除文件
	 * 
	 * @param fileName
	 * @return
	 */
	public Boolean deleteObject(String fileName) {
		try {
			ossClient.deleteObject(ossConfig.getBucketName(), fileName);
			return true;
		} catch (Exception e) {
			e.printStackTrace();
			return false;
		}
	}

	/**
	 * 查询文件列表(默认100条)
	 * 
	 * @return
	 */
	public List<OSSObjectSummary> listObjects() {
		ObjectListing listObjects = ossClient.listObjects(ossConfig.getBucketName());
		List<OSSObjectSummary> fileList = listObjects.getObjectSummaries();
		return fileList;
	}

	/**
	 * 查询文件列表(默认值100条,最大值1000条)
	 * 
	 * @param maxKeys 要查询的条数
	 * @return
	 */
	public List<OSSObjectSummary> listObjects(Integer maxKeys) {
		if (maxKeys == null) {
			maxKeys = 100;
		}
		ObjectListing listObjects = ossClient
				.listObjects(new ListObjectsRequest(ossConfig.getBucketName()).withMaxKeys(maxKeys));
		List<OSSObjectSummary> fileList = listObjects.getObjectSummaries();
		return fileList;
	}

	/**
	 * 查询指定前缀的文件列表
	 * 
	 * @param prefix 前缀
	 * @return
	 */
	public List<OSSObjectSummary> listObjects(String prefix) {
		ObjectListing listObjects = ossClient.listObjects(ossConfig.getBucketName(), prefix);
		List<OSSObjectSummary> fileList = listObjects.getObjectSummaries();
		return fileList;
	}

	/**
	 * 下载文件
	 * 
	 * @param outputStream
	 * @param fileName
	 */
	public void download(OutputStream outputStream, String fileName) {
		try {
			OSSObject ossObject = ossClient.getObject(ossConfig.getBucketName(), fileName);
			BufferedInputStream in = new BufferedInputStream(ossObject.getObjectContent());
			BufferedOutputStream out = new BufferedOutputStream(outputStream);
			byte[] buffer = new byte[1024];
			int lenght = 0;
			while ((lenght = in.read(buffer)) != -1) {
				out.write(buffer, 0, lenght);
			}
			if (out != null) {
				out.flush();
				out.close();
			}
			if (in != null) {
				in.close();
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	/**
	 * 生成下载链接
	 * 
	 * @param fileName
	 * @return
	 */
	public String getUrl(String fileName) {
		if (!StringUtils.isEmpty(fileName)) {
			// 设置URL过期时间为10年 3600l*1000*24*365*10
			Date expiration = new Date(new Date().getTime() + EXPIRATION);
			// 生成URL
			URL url = ossClient.generatePresignedUrl(ossConfig.getBucketName(), fileName, expiration);
			if (url != null) {
				return url.toString();
			}
		}
		return null;
	}

	/**
	 * 上传文件
	 * 
	 * @param inputStream
	 * @param fileName
	 * @return
	 */
	public String putObject(InputStream inputStream, String fileName) {
		try {
			// 文件扩展名
			String fileSuffix = fileName.substring(fileName.lastIndexOf(".")).toLowerCase();
			// 新的文件名
			fileName = PATH + new Random().nextInt(10000) + System.currentTimeMillis() + fileSuffix;

			// 创建上传Object的Metadata
			ObjectMetadata objectMetadata = new ObjectMetadata();
			// 上传的文件的长度
			objectMetadata.setContentLength(inputStream.available());
			// 指定该Object被下载时的网页的缓存行为
			objectMetadata.setCacheControl("no-cache");
			// 指定该Object下设置Header
			objectMetadata.setHeader("Pragma", "no-cache");
			// 指定该Object被下载时的内容编码格式
			objectMetadata.setContentEncoding("utf-8");
			// 文件的MIME,定义文件的类型及网页编码,决定浏览器将以什么形式、什么编码读取文件。如果用户没有指定则根据Key或文件名的扩展名生成,
			// 如果没有扩展名则填默认值application/octet-stream
			objectMetadata.setContentType(getcontentType(fileSuffix));
			// 指定该Object被下载时的名称(指示MINME用户代理如何显示附加的文件,打开或下载,及文件名称)
			objectMetadata.setContentDisposition("inline;filename=" + fileName);

			// 上传文件
			ossClient.putObject(ossConfig.getBucketName(), fileName, inputStream, objectMetadata);
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			try {
				if (inputStream != null) {
					inputStream.close();
				}
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		return this.ossConfig.getUrlPrefix() + fileName;
	}

	/**
	 * 获取文件名
	 * 
	 * @param muFile   文件
	 * @param isRetain 是否保留源文件名
	 * @return 返回文件名,以当前年月日作为前缀路径
	 */
	private String getFilePathName(MultipartFile file, boolean isRetain) {
		String fileName = file.getOriginalFilename();
		String name = fileName;
		String fileSuffix = "";
		if (fileName.indexOf('.') != -1) {
			name = fileName.substring(0, fileName.indexOf('.'));
			fileSuffix = fileName.substring(fileName.lastIndexOf("."));
		}

		LocalDate date = LocalDate.now();
		StringBuilder filePathName = new StringBuilder(PATH);
		filePathName.append(date.getYear());
		filePathName.append("/");
		filePathName.append(date.getMonthValue());
		filePathName.append("/");
		filePathName.append(date.getDayOfMonth());
		filePathName.append("/");
		// 添加随机后缀
		Random r = new Random();
		int pix = r.ints(1, (100 + 1)).findFirst().getAsInt();
		filePathName.append(System.currentTimeMillis());
		filePathName.append("" + pix);
		// 文件名超过32字符则截取
		if (isRetain) {
			filePathName.append("_");
			if (name.length() >= 32) {
				name = name.substring(0, 32);
			}
			filePathName.append(name);
		}
		filePathName.append(fileSuffix);
		return filePathName.toString();
	}

	/**
	 * 处理文件扩展名
	 * 
	 * @param fileSuffix
	 * @return
	 */
	public String getcontentType(String fileSuffix) {
		if (fileSuffix.equalsIgnoreCase(".bmp")) {
			return "image/bmp";
		}
		if (fileSuffix.equalsIgnoreCase(".gif")) {
			return "image/gif";
		}
		if (fileSuffix.equalsIgnoreCase(".jpeg") || fileSuffix.equalsIgnoreCase(".jpg")
				|| fileSuffix.equalsIgnoreCase(".png")) {
			return "image/jpeg";
		}
		if (fileSuffix.equalsIgnoreCase(".html")) {
			return "text/html";
		}
		if (fileSuffix.equalsIgnoreCase(".txt")) {
			return "text/plain";
		}
		if (fileSuffix.equalsIgnoreCase(".vsd")) {
			return "application/vnd.visio";
		}
		if (fileSuffix.equalsIgnoreCase(".pptx") || fileSuffix.equalsIgnoreCase(".ppt")) {
			return "application/vnd.ms-powerpoint";
		}
		if (fileSuffix.equalsIgnoreCase(".docx") || fileSuffix.equalsIgnoreCase(".doc")) {
			return "application/msword";
		}
		if (fileSuffix.equalsIgnoreCase(".xml")) {
			return "text/xml";
		}
		// 默认返回类型
		return "image/jpeg";
	}

	/**
	 * 创建存储空间
	 * 
	 * @param bucketName
	 * @return
	 */
	public boolean createBucket(String bucketName) {
		try {
			boolean flag = ossClient.doesBucketExist(bucketName);
			if (!flag) {
				ossClient.createBucket(bucketName);
				// 设置存储空间的权限为公共读,默认是私有。
				ossClient.setBucketAcl(bucketName, CannedAccessControlList.PublicRead);
				return true;
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		return false;
	}

	/**
	 * 删除存储空间
	 * 
	 * @param bucketName
	 * @return
	 */
	public boolean deleteBucket(String bucketName) {
		try {
			boolean flag = ossClient.doesBucketExist(bucketName);
			if (flag) {
				ossClient.deleteBucket(bucketName);
				return true;
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		return false;
	}

	/**
	 * 列举所有的存储空间(名称)
	 * 
	 * @return
	 */
	public List<String> listBuckets() {
		List<Bucket> buckets = ossClient.listBuckets();
		List<String> bucketNames = new ArrayList<>();
		for (Bucket bucket : buckets) {
			bucketNames.add(bucket.getName());
		}
		return bucketNames;
	}

}

图像工具类

需要添加依赖

<dependency>
	<groupId>net.coobird</groupId>
	<artifactId>thumbnailator</artifactId>
	<version>0.4.11</version>
</dependency>
/**
 * 图像工具类
 */
public class ImageUtil {
	// 压缩率
	private static final transient float IMAGE_RATIO = 0.1f;
	// 压缩最大宽度
	private static final transient int IMAGE_WIDTH = 800;
	// 水印透明度
	private static float alpha = 0.3f;
	// 水印文字字体
	private static Font font = new Font("PingFang SC Regular", Font.PLAIN, 36);
	// 水印文字颜色
	private static Color color = new Color(111, 111, 111);
	// 水印文字内容
	private static final String text = "这是一个水印文本";
	// 水印之间的间隔
	private static final int XMOVE = 80;
	// 水印之间的间隔
	private static final int YMOVE = 80;

	/**
	 * 压缩图像
	 * 
	 * @param image
	 * @return
	 * @throws IOException
	 */
	public static BufferedImage compress(BufferedImage image) throws IOException {
		Thumbnails.Builder<BufferedImage> imageBuilder = Thumbnails.of(image).outputQuality(IMAGE_RATIO);
		if (image.getWidth() > IMAGE_WIDTH) {
			return imageBuilder.width(IMAGE_WIDTH).asBufferedImage();
		} else {
			return imageBuilder.scale(1).asBufferedImage();
		}
	}

	/**
	 * 图像添加水印
	 * 
	 * @param
	 * @return
	 */
	public static BufferedImage setWatermark(BufferedImage image) throws IOException {
		return Thumbnails.of(image).outputQuality(IMAGE_RATIO).scale(1)
				.watermark(Positions.BOTTOM_RIGHT, createWatermark(text, image.getWidth(), image.getHeight()), alpha)
				.asBufferedImage();
	}

	/**
	 * 根据文件扩展名判断文件是否图片格式
	 * 
	 * @return
	 */
	public static boolean isImage(String fileName) {
		String[] imageExtension = new String[] { "jpeg", "jpg", "gif", "bmp", "png" };

		for (String e : imageExtension)
			if (getFileExtention(fileName).toLowerCase().equals(e))
				return true;

		return false;
	}

	/**
	 * 获取文件后缀名称
	 * 
	 * @param fileName
	 * @return
	 */
	public static String getFileExtention(String fileName) {
		String extension = fileName.substring(fileName.lastIndexOf(".") + 1);
		return extension;
	}

	/**
	 * 根据图片对象获取对应InputStream
	 *
	 * @param image
	 * @param readImageFormat
	 * @return
	 * @throws IOException
	 */
	public static InputStream getInputStream(BufferedImage image, String readImageFormat) throws IOException {
		ByteArrayOutputStream os = new ByteArrayOutputStream();
		ImageIO.write(image, readImageFormat, os);
		InputStream is = new ByteArrayInputStream(os.toByteArray());
		os.close();
		return is;
	}

	/**
	 * 创建水印图片
	 * 
	 * @param text   水印文字
	 * @param width  图片宽
	 * @param height 图片高
	 * @return
	 */
	public static BufferedImage createWatermark(String text, int width, int height) {
		BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
		// 2.获取图片画笔
		Graphics2D g = image.createGraphics();
		// ---------- 增加下面的代码使得背景透明 -----------------
		image = g.getDeviceConfiguration().createCompatibleImage(width, height, Transparency.TRANSLUCENT);
		g.dispose();
		g = image.createGraphics();
		// ---------- 背景透明代码结束 -----------------
		// 6、处理文字
		AttributedString ats = new AttributedString(text);
		ats.addAttribute(TextAttribute.FONT, font, 0, text.length());
		// 7、设置对线段的锯齿状边缘处理
		g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
		g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
		// 8、设置水印旋转
		g.rotate(Math.toRadians(-30));
		// 9、设置水印文字颜色
		g.setColor(color);
		// 10、设置水印文字Font
		g.setFont(font);
		// 11、设置水印文字透明度
		g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_ATOP, alpha));
		g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER));
		/**
		 * 水印铺满图片 计算水印位置
		 */
		int x = -width / 2;
		int y = -height / 2;
		int[] arr = getWidthAndHeight(text, font);
		int markWidth = arr[0];// 字体长度
		int markHeight = arr[1];// 字体高度
		// 循环添加水印
		while (x < width * 1.5) {
			y = -height / 2;
			while (y < height * 1.5) {
				g.drawString(text, x, y);

				y += markHeight + YMOVE;
			}
			x += markWidth + XMOVE;
		}
		// 13、释放资源
		g.dispose();
		return image;
	}

	/**
	 * 计算字体宽度及高度
	 * 
	 * @param text
	 * @param font
	 * @return
	 */
	private static int[] getWidthAndHeight(String text, Font font) {
		Rectangle2D r = font.getStringBounds(text,
				new FontRenderContext(AffineTransform.getScaleInstance(1, 1), false, false));
		int unitHeight = (int) Math.floor(r.getHeight());//
		// 获取整个str用了font样式的宽度这里用四舍五入后+1保证宽度绝对能容纳这个字符串作为图片的宽度
		int width = (int) Math.round(r.getWidth()) + 1;
		// 把单个字符的高度+3保证高度绝对能容纳字符串作为图片的高度
		int height = unitHeight + 3;
		return new int[] { width, height };
	}

}

代码托管:springboot_oss

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章