Springboot使用Thumbnailator压缩图片上传到阿里云OSS(无损压缩) - Go语言中文社区

Springboot使用Thumbnailator压缩图片上传到阿里云OSS(无损压缩)


前提:

图片的压缩大致有两种,一种是将图片的尺寸压缩小,另一种是尺寸不变,将压缩质量,一般对于项目我们需要第一种,即用户上传一张分辨率为3840 × 2160的图片,通过上传图片接口后上传到OSS上的图片分辨率会变成1920×1080(如3840 × 2160的图片大小为11.4M,上传后的图片大概会为1.9M),此时上传后到OSS的图片和原图质量上一致,也就是说看上去只的大小的区别,清晰度上没有任何区别,如还希望图片再小点再进行质量压缩。

整体思路:

  1. 用户点击上传图片按钮,调用上传接口,通过springboot的MultipartFile接口接收到文件,再将MultipartFile转化成一个文件放到项目中待使用(路径自己指定)。
  2. 通过指定文件路径获取到该文件(contextPath是步骤1中转存图片的文件路径),使用Thumbnails对图片进行尺寸压缩和格式转换(将图片转为jpg),然后将压缩后的图片替换步骤1中的图片。
  3. 再次通过File tempFile = new File(contextPath)获取压缩后的图片文件,将图片转为inputStream流上传至OSS,将项目中的图片文件删除,完成。

测试效果:

调用上传图片接口:
在这里插入图片描述
接口返回结果:

{
    "success": true,
    "status": "200",
    "msg": "上传图片成功",
    "data": "bookService/pictureUploadTest/b42dd004ddae4b329b91de0472b26163/原图11.4M(3840×2160.jpg"
}

OSS上压缩后的图片

在这里插入图片描述

项目代码:

控制器

    /**
     * 上传图片(通用)
     *
     * @param file         文件
     * @param id           图书id
     * @param folderSecond 设置二级图片路径(例bookUpload/)
     * @return
     */
    @RequestMapping(value = "pictureFile", method = RequestMethod.POST)
    @ResponseBody
    public JsonResult uploadPicture(MultipartFile file, String id, String folderSecond) {
        try {
            String pictureUrl = uploadService.uploadPicture(file, id, folderSecond);
            log.info("上传后的图片地址:" + pictureUrl);
            return renderSuccess("上传图片成功", pictureUrl);
        } catch (ProgramException p) {
            log.error("上传图片失败." + p.getMessage());
            return renderError(p.getMessage());
        } catch (Exception e) {
            log.error("上传图片失败." + e.getMessage());
            return renderError("上传图片失败");
        }
    }

实现类:
注意:使用Thumbnailator进行格式转换的时候如果图片格式是png,在转成jpg后会出现图片变红的bug,所以此处的处理是不处理,也就是如果图片是后缀是png的话,不进行格式转换,只进行大小压缩


   @Override
    public String uploadPicture(MultipartFile multipartfile, String id, String folderSecond) throws Exception {
        if (multipartfile == null || id == null || folderSecond == null) {
            throw new ProgramException("上传图片参数不合法");
        }
        if (multipartfile.getSize() > 50 * 1024 * 1024) {
            throw new ProgramException("上传图片大小不能超过50M!");
        }

        //设置统一图片后缀名
        String suffixName;

        //获取图片文件名(不带扩展名的文件名)
        String prefixName = getFileNameWithoutEx(multipartfile.getOriginalFilename());

        //获取图片后缀名,判断如果是png的话就不进行格式转换,因为Thumbnails存在转png->jpg图片变红bug
        String suffixNameOrigin = getExtensionName(multipartfile.getOriginalFilename());

        if ("png".equals(suffixNameOrigin)) {
            suffixName = "png";
        } else {
            suffixName = "jpg";
        }

        //图片存储文件夹
        String filePath = "web/src/main/resources/";

        //图片在项目中的地址(项目位置+图片名,带后缀名)
        String contextPath = filePath + prefixName + "." + suffixName;
        //存的项目的中模版图片
        File tempFile = null;
        //上传时从项目中拿到的图片
        File f = null;
        InputStream inputStream = null;
        try {
	        //图片在项目中的地址(项目位置+图片名,带后缀名)
	        tempFile = new File(contextPath);
	        if (!tempFile.exists()) {
	            //生成图片文件
	            FileUtils.copyInputStreamToFile(multipartfile.getInputStream(), tempFile);
	        }
	
	        /*
	         * size(width,height) 若图片横比1920小,高比1080小,不变
	         * 若图片横比1920小,高比1080大,高缩小到1080,图片比例不变 若图片横比1920大,高比1080小,横缩小到1920,图片比例不变
	         * 若图片横比1920大,高比1080大,图片按比例缩小,横为1920或高为1080
	         * 图片格式转化为jpg,质量不变
	         */
	         BufferedImage image = ImageIO.read(multipartfile.getInputStream());
	 		if (image.getHeight() > 1080 || image.getWidth() > 1920) {
	            if (!"png".equals(suffixName)) {
	                Thumbnails.of(contextPath).size(1920, 1080).outputQuality(1f).outputFormat("jpg").toFile(contextPath);
	            } else {
	                Thumbnails.of(contextPath).size(1920, 1080).outputQuality(1f).toFile(contextPath);
	            }
	        } else {
	            if (!"png".equals(suffixName)) {
	                Thumbnails.of(contextPath).outputQuality(1f).scale(1f).outputFormat("jpg").toFile(contextPath);
	            } else {
	                Thumbnails.of(contextPath).outputQuality(1f).scale(1f).toFile(contextPath);
	            }
	        }
	
	        //获取压缩后的图片
	        f = new File(contextPath);
	        inputStream = new FileInputStream(f);
	
	        //设置三级文件夹名
	        String folderThird = id + "/";
	
	        //设置OSS上的二级文件目录
	        String folderPath = folderSecond + folderThird;
	
	        //设置图片存储在oss上的名字
	        String fileName = prefixName + "." + suffixName;
	
            //上传图片到OSS,返回图书路径
            String resultUrl = AliyunOSSClientUtil.uploadImg2Oss(inputStream, folderPath, fileName);
            return resultUrl;
        } catch (Exception e) {
            throw new ProgramException("图片上传失败");
        } finally {
 			 //将临时文件删除
            tempFile.delete();
            f.delete();
            inputStream.close();	
        }
    }

/**
     * 获取文件扩展名
     *
     * @param filename 文件名
     * @return
     */
    public static String getExtensionName(String filename) {
        if ((filename != null) && (filename.length() > 0)) {
            int dot = filename.lastIndexOf('.');
            if ((dot > -1) && (dot < (filename.length() - 1))) {
                return filename.substring(dot + 1);
            }
        }
        return filename;
    }

	//删除oss上的文件(该博客没有涉及到该方法,可以跳过)
	@Override
    public boolean deleteOssFile(String id) throws Exception {
        if (id == null) {
            throw new ProgramException("删除OSS文件参数不合法");
        }
        //设置文件位置的二级文件夹名
        String folderSecond = "bookUpload/";

        //设置三级文件夹名
        String folderThird = id;

        //设置OSS上要删除的的二级文件目录(例:client/45e233d07c664b93b7bb35331285a8d8)
        String folderPath = folderSecond + folderThird;

        //删除OSS上的文件(文件夹+文件)
        return AliyunOSSClientUtil.deleteFile2Oss(folderPath);
    }

 /**
     * 获取不带扩展名的文件名
     *
     * @param filename 文件
     * @return
     */
    private static String getFileNameWithoutEx(String filename) {
        if ((filename != null) && (filename.length() > 0)) {
            int dot = filename.lastIndexOf('.');
            if ((dot > -1) && (dot < (filename.length()))) {
                return filename.substring(0, dot);
            }
        }
        return filename;
    }

自定义OSS工具类


package com.welsee.tools;

import com.aliyun.oss.OSSClient;
import com.aliyun.oss.model.*;
import com.welsee.exception.ProgramException;
import lombok.extern.slf4j.Slf4j;

import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;


/**
 * 阿里云OSS相关java API
 *
 * @author lixinyu
 */
@Slf4j
public class AliyunOSSClientUtil {


    //阿里云API的内或外网域名
    private static String ENDPOINT;
    //阿里云API的密钥Access Key ID
    private static String ACCESS_KEY_ID;
    //阿里云API的密钥Access Key Secret
    private static String ACCESS_KEY_SECRET;
    //阿里云API的bucket名称
    private static String BUCKET_NAME;
    //阿里云API的文件夹名称
    private static String FOLDER;

    //初始化属性
    static {
        ENDPOINT = OSSClientConstants.ENDPOINT;
        ACCESS_KEY_ID = OSSClientConstants.ACCESS_KEY_ID;
        ACCESS_KEY_SECRET = OSSClientConstants.ACCESS_KEY_SECRET;
        BUCKET_NAME = OSSClientConstants.BUCKET_NAME;
        FOLDER = OSSClientConstants.FOLDER;
    }

    /**
     * 获取阿里云OSS客户端对象
     *
     * @return ossClient
     */
    private static OSSClient getOSSClient() {
//        log.info("实例化阿里云OSS对象===============" + ENDPOINT + "," + ACCESS_KEY_ID + "," + ACCESS_KEY_SECRET);
        return new OSSClient(ENDPOINT, ACCESS_KEY_ID, ACCESS_KEY_SECRET);
    }

    /**
     * 通过文件名判断并获取OSS服务文件上传时文件的contentType
     *
     * @param fileName 文件名
     * @return 文件的contentType
     */
    private static String getContentType(String fileName) {
        //文件的后缀名
        String fileExtension = fileName.substring(fileName.lastIndexOf("."));
        if (".bmp".equalsIgnoreCase(fileExtension)) {
            return "image/bmp";
        }
        if (".gif".equalsIgnoreCase(fileExtension)) {
            return "image/gif";
        }
        if (".jpeg".equalsIgnoreCase(fileExtension) || ".jpg".equalsIgnoreCase(fileExtension) || ".png".equalsIgnoreCase(fileExtension)) {
            return "image/jpeg";
        }
        if (".html".equalsIgnoreCase(fileExtension)) {
            return "text/html";
        }
        if (".txt".equalsIgnoreCase(fileExtension)) {
            return "text/plain";
        }
        if (".vsd".equalsIgnoreCase(fileExtension)) {
            return "application/vnd.visio";
        }
        if (".ppt".equalsIgnoreCase(fileExtension) || "pptx".equalsIgnoreCase(fileExtension)) {
            return "application/vnd.ms-powerpoint";
        }
        if (".doc".equalsIgnoreCase(fileExtension) || "docx".equalsIgnoreCase(fileExtension)) {
            return "application/msword";
        }
        if (".xml".equalsIgnoreCase(fileExtension)) {
            return "text/xml";
        }
        //默认返回类型
        return "image/jpeg";
    }


    /**
     * 封装上传到OSS服务器方法 如果同名文件会覆盖服务器上的
     *
     * @param inputStream 文件流
     * @param folderPath  OSS目录下的二级文件名
     * @param fileName    文件名称 包括后缀名
     * @return 出错返回"" ,唯一MD5数字签名
     */
    public static String uploadImg2Oss(InputStream inputStream, String folderPath, String fileName) throws Exception {
        String result = "";
        // 创建上传Object的Metadata
        ObjectMetadata metadata = new ObjectMetadata();
        metadata.setContentLength(inputStream.available());
        //指定该Object被下载时的网页的缓存行为
        metadata.setCacheControl("no-cache");
        //指定该Object下设置Header
        metadata.setHeader("Pragma", "no-cache");
        //文件的MIME,定义文件的类型及网页编码,决定浏览器将以什么形式、什么编码读取文件。如果用户没有指定则根据Key或文件名的扩展名生成,
        //如果没有扩展名则填默认值application/octet-stream
        metadata.setContentType(getContentType(fileName));
        //指定该Object被下载时的名称(指示MINME用户代理如何显示附加的文件,打开或下载,及文件名称)
        metadata.setContentDisposition("inline;filename=" + fileName);
        String resultUrl = FOLDER + folderPath + fileName;
        // 上传文件
        PutObjectResult putResult = getOSSClient().putObject(BUCKET_NAME, resultUrl, inputStream, metadata);
        // 设置文件的访问权限为公共读。
        getOSSClient().setObjectAcl(BUCKET_NAME, resultUrl, CannedAccessControlList.PublicRead);

        if (!"".equals(putResult.getETag())) {
            result = resultUrl;
            log.info("上传后的图片MD5数字唯一签名:" + putResult.getETag()); //可以用来验证上传的资源是否为同一个(暂时没用到)
            log.info("上传阿里云OSS服务器成功");

        } else {
            log.error("上传阿里云OSS服务器异常");
        }
        inputStream.close();
        getOSSClient().shutdown();
        return result;
    }

   /**
     * 删除oss上的文件,文件夹+文件夹里面的所的文件(该博客没有涉及到该方法,可以跳过)
     *
     * @param folderPath 设置OSS上要删除的的二级文件目录(例:(client/45e233d07c664b93b7bb35331285a8d8))
     * @return
     */
    public static boolean deleteFile2Oss(String folderPath) {
        
        //oss项目名+图书位置(bookService/bookUpload/45e233d07c664b93b7bb35331285a8d8)
        String prefix = FOLDER + folderPath;

        // 列举文件。 如果不设置KeyPrefix,则列举存储空间下所有的文件。KeyPrefix,则列举包含指定前缀的文件。
        ObjectListing objectListing = getOSSClient().listObjects(new ListObjectsRequest(BUCKET_NAME).withPrefix(prefix));
        List<String> keys = new ArrayList<>();
        List<OSSObjectSummary> sums = objectListing.getObjectSummaries();
        for (OSSObjectSummary s : sums) {
            keys.add(s.getKey());
        }

        //如果OSS文件上有文件的话删除,没有直接跳过
        if (keys.size() > 0) {
            // 删除文件夹内的文件。
            DeleteObjectsResult deleteObjectsResult = getOSSClient().deleteObjects(new DeleteObjectsRequest(BUCKET_NAME).withKeys(keys));
            deleteObjectsResult.getDeletedObjects();

            //删除文件夹
            List<String> key = new ArrayList<>();
            key.add(prefix);
            getOSSClient().deleteObjects(new DeleteObjectsRequest(BUCKET_NAME).withKeys(key));

        }
        // 关闭OSSClient。
        getOSSClient().shutdown();
        
        return true;
    }

    /**
     * 删除oss一个文件夹中的无用图片,保留一个图片(该博客没有涉及到该方法,可以跳过)
     *
     * @param picturePath 有用的图片路径
     * @return
     */
    public static boolean deletePictureUseless(String picturePath) throws Exception {

        if (picturePath == null) {
            throw new ProgramException("删除oss无用图片参数不合法");
        }

        //文件夹路径(bookService/bookUpload/0c6fd6fbac33466e8b26e73115f80edd)
        String filePath = picturePath.substring(0, picturePath.lastIndexOf("/"));

        //列举所有的图片
        ObjectListing objectListing = getOSSClient().listObjects(new ListObjectsRequest(BUCKET_NAME).withPrefix(filePath));
        List<String> keys = new ArrayList<>();
        List<OSSObjectSummary> sums = objectListing.getObjectSummaries();
        for (OSSObjectSummary s : sums) {
            //判断是否是图片,只删无用图片, 不删图书
            if ("jpg".equals(s.getKey().substring(s.getKey().lastIndexOf(".") + 1))) {
                if (!picturePath.equals(s.getKey())) {
                    keys.add(s.getKey());
                }
            }
        }
        log.info("要删除的图片为===============" + keys.toString());
        // 删除无用图片
        if (keys.size() > 0) {
            DeleteObjectsResult deleteObjectsResult = getOSSClient().deleteObjects(new DeleteObjectsRequest(BUCKET_NAME).withKeys(keys));
            deleteObjectsResult.getDeletedObjects();
        }
        return true;
    }
}

OSS配置文件:

package com.welsee.tools;


import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;

/**
 * @author lixinyu
 * @class:OSSClientConstants
 * @descript:阿里云注册用户基本常量
 * @date:2018年11月06日 下午5:52:34
 */
@Configuration
public class OSSClientConstants {
    //阿里云API的外网域名
    public static String ENDPOINT;
    //阿里云API的密钥Access Key ID
    public static String ACCESS_KEY_ID;
    //阿里云API的密钥Access Key Secret
    public static String ACCESS_KEY_SECRET;
    //阿里云API的bucket名称
    public static String BUCKET_NAME;
    //阿里云API的文件夹名称
    public static String FOLDER;

    @Value("${OSS_ENDPOINT}")
    public void setENDPOINT(String ENDPOINT) {
        OSSClientConstants.ENDPOINT = ENDPOINT;
    }

    @Value("${OSS_ACCESS_KEY_ID}")
    public void setAccessKeyId(String accessKeyId) {
        OSSClientConstants.ACCESS_KEY_ID = accessKeyId;
    }

    @Value("${OSS_ACCESS_KEY_SECRET}")
    public void setAccessKeySecret(String accessKeySecret) {
        OSSClientConstants.ACCESS_KEY_SECRET = accessKeySecret;
    }

    @Value("${OSS_BUCKET_NAME}")
    public void setBucketName(String bucketName) {
        BUCKET_NAME = bucketName;
    }

    @Value("${OSS_FOLDER}")
    public void setFOLDER(String FOLDER) {
        OSSClientConstants.FOLDER = FOLDER;
    }
}
版权声明:本文来源CSDN,感谢博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/weixin_39973810/article/details/86545054
站方申明:本站部分内容来自社区用户分享,若涉及侵权,请联系站方删除。

0 条评论

请先 登录 后评论

官方社群

GO教程

猜你喜欢