java只会上传图片已经out啦,优化存储空间!!!

前言

最近在开发自己的个人博客,其中涉及有个功能是用户可以发布碎片的,能够上传图片,但是上传后过大,想着能不能进行压缩,然后搜集了一些资料后,一开始选择了缩略图,但是图片本身也得存储,也是占用了很大的空间,客户端在查看时需要加载很久,继续搜集资料,最终得出了自己的解决方案,将图片转为webp格式并结合缩略图一同存储。

成果

极大的优化了存储空间,提升了网页的加载速度,并且图片的质量被没有损失太多。

一张4m的图片上传后压缩的情况

   
webp 格式只有280Kb                                                缩略图格式更是只有5kb

至于缩略图的作用,小图展示时采用缩略图的方式,大图时再加载webp格式,可进一步优化用户体验

欢迎访问~
个人博客icon-default.png?t=N7T8https://bibinliu.com

优化步骤

要转为webp格式以及生成缩略图需要导入一下两个依赖

        <!-- 图片压缩-->
        <dependency>
            <groupId>net.coobird</groupId>
            <artifactId>thumbnailator</artifactId>
            <version>0.4.14</version>
        </dependency>
        <!-- webp-imageio 依赖 -->
        <dependency>
            <groupId>org.sejda.imageio</groupId>
            <artifactId>webp-imageio</artifactId>
            <version>0.1.6</version>
        </dependency>

关键代码

getUniqueFileName的详细代码可看完整接口代码

File.separator等价于 "/"

 public Result<?> uploadImg(MultipartFile file,Long userId) {
        //用户文件夹
        String userPath = "/path/to/upload/dir" + File.separator + userId +File.separator;
        String serverUrl = "http://localhost:8080"
        // 获取上传文件的名字,生成唯一的名称
        String fileName = file.getOriginalFilename();
        String uniqueFileName = getUniqueFileName(fileName);
        //webp文件名
        String webpUrl = uniqueFileName.substring(0, uniqueFileName.lastIndexOf(".")) + ".webp";
        try {
            Path path = Paths.get(userPath + webpUrl);
            // 获取原始文件的编码
            BufferedImage image = ImageIO.read(file.getInputStream());
            // 创建WebP ImageWriter实例
            ImageWriter writer = ImageIO.getImageWritersByMIMEType("image/webp").next();
            // 配置编码参数
            WebPWriteParam writeParam = new WebPWriteParam(writer.getLocale());
            // 设置压缩模式
            writeParam.setCompressionMode(WebPWriteParam.MODE_DEFAULT);
            // 配置ImageWriter输出
            writer.setOutput(new FileImageOutputStream(path.toFile()));
            // 进行编码,重新生成新图片
            writer.write(null, new IIOImage(image, null, null), writeParam);

            // 压缩成缩略图并保存
            File thumbnailFile = new File(userPathCurrentMonth+ File.separator +  "thumbnail_" + uniqueFileName);
            Thumbnails.of(path.toFile())
                    .size(320, 180)
                    .toFile(thumbnailFile);

            // 可访问的路径
            String userPathCurrentMonthUrl = serverUrl + "/files" + File.separator + userId +File.separator

            // 生成文件的URL
            String fileUrl = userPathCurrentMonthUrl+ webpUrl;
            // 生成缩略图的URL
            String thumbnailUrl = userPathCurrentMonthUrl + "thumbnail_" + uniqueFileName;
            return Result.success();

        } catch (Exception e) {
            return Result.error("上传文件失败");
        }
    }

webConfig配置

用于配置Web应用程序的资源处理,使程序可以访问特定目录下的文件

package com.llpy.textservice.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

/**
 * web配置
 *
 * @author llpy
 * @date 2024/07/04
 */
@Configuration
public class WebConfig implements WebMvcConfigurer {

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        String uploadDir = "/path/to/upload/dir";
        registry.addResourceHandler("/files/**")
                .addResourceLocations("file:" + uploadDir + "/");
    }
}

完整接口代码

我这里是去限制了每个用户的空间大小,根据不同的用户权限限制用户的存储空间,并且我是部署在服务器上的,我的路径以及访问链接都通过配置文件进行读取

@Service
public class ImagesServiceImpl implements ImagesService {

    @Value("${file.upload-dir}")
    private String uploadDir;

    @Value("${server.url}")
    private String serverUrl;

    @Autowired
    private UserService userService;

    @Autowired
    private PhotoWallMapper photoWallMapper;

    @Override
    public Result<?> uploadImg(MultipartFile file,Long userId) {
        if (file.isEmpty()) {
            return Result.error("文件为空");
        }
        //如果不是图片,不给上传
        if (!Objects.requireNonNull(file.getContentType()).startsWith("image")) {
            return Result.error("文件格式不正确");
        }

        //获取用户大小限制
        UserDto2 user = userService.getUser(userId);
        long maxSize = 0L;
        for (CommonEnum value : CommonEnum.values()) {
            if(value.getKey().equals(user.getRoleName())){
                maxSize = value.getMaxSize();
            }
        }
        //用户文件夹
        String userPath = uploadDir + File.separator + userId +File.separator;
        // 获取当前日期并格式化为 "yyyy-MM" 格式
        String folderByMonth = new SimpleDateFormat("yyyy-MM").format(new Date());
        // 用户当前月份的文件夹
        String userPathCurrentMonth =userPath+folderByMonth;
        // 获取上传文件的名字,生成唯一的名称
        String fileName = file.getOriginalFilename();
        String uniqueFileName = getUniqueFileName(fileName);
        //webp文件名
        String webpUrl = uniqueFileName.substring(0, uniqueFileName.lastIndexOf(".")) + ".webp";
        try {
            Path path = Paths.get(userPathCurrentMonth + File.separator + webpUrl);
            // 检查用户文件夹大小
            Path userFolder = Paths.get(userPath);
            long folderSize = getFolderSize(userFolder);
            if (folderSize + file.getSize() > maxSize) {
                return Result.error("您的剩余空间" + (maxSize - folderSize) / 1024 / 1024 +"MB,不足以上传该文件,请联系管理员扩容空间");
            }


            // 确保上传目录存在
            Path uploadPath = Paths.get(userPathCurrentMonth);
            if (!Files.exists(uploadPath)) {
                Files.createDirectories(uploadPath);
            }
            // 获取原始文件的编码
            BufferedImage image = ImageIO.read(file.getInputStream());
            // 创建WebP ImageWriter实例
            ImageWriter writer = ImageIO.getImageWritersByMIMEType("image/webp").next();
            // 配置编码参数
            WebPWriteParam writeParam = new WebPWriteParam(writer.getLocale());
            // 设置压缩模式
            writeParam.setCompressionMode(WebPWriteParam.MODE_DEFAULT);
            // 配置ImageWriter输出
            writer.setOutput(new FileImageOutputStream(path.toFile()));
            // 进行编码,重新生成新图片
            writer.write(null, new IIOImage(image, null, null), writeParam);

            // 压缩成缩略图并保存
            // 生成缩略图
            File thumbnailFile = new File(userPathCurrentMonth+ File.separator +  "thumbnail_" + uniqueFileName);
            Thumbnails.of(path.toFile())
                    .size(320, 180)
                    .toFile(thumbnailFile);

            // 可访问的路径
            String userPathCurrentMonthUrl = serverUrl + "/files" + File.separator + userId +File.separator +folderByMonth + File.separator;
            // 生成文件的URL
            String fileUrl = userPathCurrentMonthUrl+ webpUrl;
            // 生成缩略图的URL
            String thumbnailUrl = userPathCurrentMonthUrl + "thumbnail_" + uniqueFileName;

            // 保存到数据库
            PhotoWall photoWall = new PhotoWall();
            photoWall.setUserId(userId).setImgUrl(fileUrl).setThumbnailImgUrl(thumbnailUrl);
            photoWallMapper.insert(photoWall);
            // 返回文件URL
            return Result.success(fileUrl);

        } catch (Exception e) {
            //发生异常删除上传的图片
            File file1 = new File(userPathCurrentMonth + File.separator + uniqueFileName);
            if (file1.exists()) {
                file1.delete();
            }
            File file2 = new File(userPathCurrentMonth + File.separator + "thumbnail_" + uniqueFileName);
            if (file2.exists()) {
                file2.delete();
            }
            return Result.error("上传文件失败");
        }
    }


    /**
     * 获取唯一文件名
     *
     * @param fileName 文件名
     * @return {@code String}
     */
    private static String getUniqueFileName(String fileName) {
        String fileExtension = "";

        // 提取文件扩展名
        if (fileName != null && fileName.contains(".")) {
            fileExtension = fileName.substring(fileName.lastIndexOf("."));
        }

        // 生成唯一的文件名
        return UUID.randomUUID().toString().replace("-", "") + fileExtension;
    }

    /**
     * 获取文件夹大小
     *
     * @param folder 文件夹
     * @return long
     */
    public long getFolderSize(Path folder) {
        if (!Files.exists(folder) || !Files.isDirectory(folder)) {
            return 0;
        }
        try {
            return Files.walk(folder)
                    .filter(Files::isRegularFile)
                    .mapToLong(p -> {
                        try {
                            return Files.size(p);
                        } catch (IOException e) {
                            return 0L;
                        }
                    })
                    .sum();
        } catch (IOException e) {
            throw new RuntimeException("获取文件夹大小失败");
        }
    }

}

总结

在上传图片时,采用将图片转为webp和生成缩略图的格式,可以极大的优化用户体验以及存储空间。

  • 7
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值