Springboot实现文件上传文件压缩和前端展示

最近公司要写一个文件上传和下载的需求,之前写过很多次了,今天做个记录,希望能帮助到大家
    *Controller 代码*
@RestController
@RequestMapping("/upload")
@Api(tags="图片接口")
public class UploadController {

    /**
     * 批量上传
     * @param files
     * @return
     */
    @ApiOperation("批量上传")
    @PostMapping
    public Result uploadImage(@RequestParam(value = "文件夹路径",required = false) String folderName,@RequestParam("files") MultipartFile[]  files){
            return  UploadUtil.uploadImage(files,folderName==null?"imgs/":folderName);
    }
    /**
     * 单个上传
     * @param file
     * @return
     */
    @ApiOperation("单个图片上传")
    @PostMapping("only")
    public Result uploadImageOnly(@RequestParam(value = "文件夹路径",required = false) String folderName,@RequestParam("file")MultipartFile  file){
        return   UploadUtil.uploadImageOnly(file,folderName==null?"imgs/":folderName);
    }
}

UploadUtil代码

import io.renren.common.exception.RenException;
import io.renren.common.utils.Result;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.PostConstruct;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.UUID;

/**
 * 文件上传
 * @Author ztz
 * @Date 2022年12月13日 0013 09:13
 * @Version 1.0
 */
@Component
public class UploadUtil {
/**
     * 本地目录,因为@Value 无法直接被注入。故采用@PostConstruct方式获取
     */
    @Value("${accessFile.location}")
    private String location;
	
    private static String staticLocation;

    @PostConstruct
    public void init() {
        staticLocation = location;
    }

    public static List<String> types = Arrays.asList("PNG", "JPG", "JPEG", "BMP", "GIF", "SVG");
    public static List<String> VIDEOTYPES = Arrays.asList("MP4", "MP3");

    /**
     * 批量上传
     *
     * @param files
     * @return
     */
    public static Result uploadImage(@RequestParam("files") MultipartFile[] files, String folderName) {
        List<String> urls = new ArrayList<>();
        try {
            for (MultipartFile multipartFile : files) {
                urls.add(getImageUrl(multipartFile, folderName));
            }
        } catch (Exception e) {
            return new Result().error(e.getMessage());
        }
        return new Result().ok(urls);
    }

    /**
     * 单文件上传
     *
     * @param file
     * @return
     */
    public static Result uploadImageOnly(MultipartFile file, String folderName) {
        String url = null;
        try {
            url = getImageUrl(file, folderName);
        } catch (Exception e) {
            return new Result().error(e.getMessage());
        }
        return new Result().ok(url);
    }

    /**
     * 音频、视频上传
     *
     * @param file
     * @return
     */
    public static Result uploadVideo(MultipartFile file, String folderName) {
        String url = null;
        try {
            url = getVideoUrl(file, folderName);
        } catch (Exception e) {
            return new Result().error(e.getMessage());
        }
        return new Result().ok(url);
    }
    private static String getVideoUrl(MultipartFile file, String folderName) throws Exception {
        String reuslt = null;
        if (file != null) {
            if (!checkFileSize(file, 50, "m")) {
                return reuslt;
            }
            String suffix = FilenameUtils.getExtension(file.getOriginalFilename());
            if (file.getInputStream() == null) {
                return reuslt;
            }
            if (!VIDEOTYPES.contains(suffix.toUpperCase())) {
                return reuslt;
            }
            try {
                //1. 使用UUID重新生成文件名,防止文件名称重复造成文件覆盖
                String fileName = UUID.randomUUID()  + "." + FilenameUtils.getExtension(file.getOriginalFilename());
                //2. 创建一个目录对象
                String folderPath = staticLocation  + (fileName != null ? folderName : "");
                File dir = new File(folderPath);
                //3. 判断当前目录是否存在
                if (!dir.exists()) {
                    //若目录不存在,需要创建
                    dir.mkdirs();
                }
                //4. 将临时文件转存到指定位置
                file.transferTo(new File(folderPath + fileName));
                return fileName;
            } catch (Exception e) {  // 上传出现问题直接抛出系统异常,不属于业务范围
                e.printStackTrace();
                throw new RenException("文件上传出错");
            }
        }
        return reuslt;
    }

    private static String getImageUrl(MultipartFile file, String folderName) throws Exception {
        String reuslt = null;
        if (file != null) {
            if (!checkFileSize(file, 10, "m")) {
                return reuslt;
            }
            String suffix = FilenameUtils.getExtension(file.getOriginalFilename());
            String contentType = file.getContentType();
            if (!contentType.contains(contentType)) {
                return reuslt;
            }
            if (!types.contains(suffix.toUpperCase())) {
                return reuslt;
            }
            //先把byte转成kb
            BigDecimal imgKb = new BigDecimal(file.getSize()).divide(new BigDecimal(1024), 2, BigDecimal.ROUND_UP);
            byte[] bytes = PicUtils.compressPicForScale(file.getBytes(), imgKb.multiply(new BigDecimal(0.8)).longValue());
            ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes);
            try {
                //1. 使用UUID重新生成文件名,防止文件名称重复造成文件覆盖
                String fileName = UUID.randomUUID() + "." +FilenameUtils.getExtension(file.getOriginalFilename());
                //2. 创建一个目录对象
                String folderPath = staticLocation + (fileName != null ? folderName : "");
                File dir = new File(folderPath);
                //3. 判断当前目录是否存在
                if (!dir.exists()) {
                    //若目录不存在,需要创建
                    dir.mkdirs();
                }
                //4. 将临时文件转存到指定位置
                File codeImgFile = new File(folderPath + fileName);
                FileUtils.copyToFile(inputStream, codeImgFile);
                return fileName;
            } catch (Exception e) {  // 上传出现问题直接抛出系统异常,不属于业务范围
                e.printStackTrace();
                throw new RenException("文件上传出错");
            }
        }
        return reuslt;
    }

    /**
     * 判断文件大小
     *
     * @param :multipartFile:上传的文件
     * @param size:                限制大小
     * @param unit:限制单位(B,K,M,G)
     * @return boolean:是否大于
     */
    public static boolean checkFileSize(MultipartFile multipartFile, int size, String unit) {
        long len = multipartFile.getSize();//上传文件的大小, 单位为字节.
        //准备接收换算后文件大小的容器
        double fileSize = 0;
        if ("B".equals(unit.toUpperCase())) {
            fileSize = (double) len;
        } else if ("K".equals(unit.toUpperCase())) {
            fileSize = (double) len / 1024;
        } else if ("M".equals(unit.toUpperCase())) {
            fileSize = (double) len / 1048576;
        } else if ("G".equals(unit.toUpperCase())) {
            fileSize = (double) len / 1073741824;
        }
        //如果上传文件大于限定的容量
        if (fileSize > size) {
            return false;
        }
        return true;
    }
}

PicUtils代码,网上找的直接拿来用了。找不到原作者见谅。net.coobird.thumbnailator.Thumbnails需要自己maven导入。这里就不帖pom文件了

import net.coobird.thumbnailator.Thumbnails;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;

/**
 * @PROJECT_NAME: water_chivalry
 * @AUTHOR: Hanson-Hsc
 * @DATE: 2020-07-27 09:08
 * @DESCRIPTION: 图片压缩工具
 * @VERSION:
 */
public class PicUtils {
  //以下是常量,按照阿里代码开发规范,不允许代码中出现魔法值
    private static final Logger logger = LoggerFactory.getLogger(PicUtils.class);
    private static final Integer ZERO = 0;
    private static final Integer ONE_ZERO_TWO_FOUR = 1024;
    private static final Integer NINE_ZERO_ZERO = 900;
    private static final Integer THREE_TWO_SEVEN_FIVE = 3275;
    private static final Integer TWO_ZERO_FOUR_SEVEN = 2047;
    private static final Double ZERO_EIGHT_FIVE = 0.85;
    private static final Double ZERO_SIX = 0.6;
    private static final Double ZERO_FOUR_FOUR = 0.44;
    private static final Double ZERO_FOUR = 0.4;

    /**
     * 根据指定大小压缩图片
     *
     * @param imageBytes  源图片字节数组
     * @param desFileSize 指定图片大小,单位kb
     * @return 压缩质量后的图片字节数组
     */
    public static byte[] compressPicForScale(byte[] imageBytes, long desFileSize) {
        if (imageBytes == null || imageBytes.length <= ZERO || imageBytes.length < desFileSize * ONE_ZERO_TWO_FOUR) {
            return imageBytes;
        }
        long srcSize = imageBytes.length;
        double accuracy = getAccuracy(srcSize / ONE_ZERO_TWO_FOUR);
        try {
            while (imageBytes.length > desFileSize * ONE_ZERO_TWO_FOUR) {
                ByteArrayInputStream inputStream = new ByteArrayInputStream(imageBytes);
                ByteArrayOutputStream outputStream = new ByteArrayOutputStream(imageBytes.length);
                Thumbnails.of(inputStream)
                        .scale(accuracy) //0-1 float 压缩大小
                        .outputQuality(accuracy) //0-1 压缩质量
                        .toOutputStream(outputStream);
                imageBytes = outputStream.toByteArray();
            }
            logger.info("图片原大小={}kb | 压缩后大小={}kb",
                    srcSize / ONE_ZERO_TWO_FOUR, imageBytes.length / ONE_ZERO_TWO_FOUR);
        } catch (Exception e) {
            logger.error("【图片压缩】msg=图片压缩失败!", e);
        }
        return imageBytes;
    }

    /**
     * 自动调节精度(经验数值)
     *
     * @param size 源图片大小
     * @return 图片压缩质量比
     */
    private static double getAccuracy(long size) {
        double accuracy;
        if (size < NINE_ZERO_ZERO) {
            accuracy = ZERO_EIGHT_FIVE;
        } else if (size < TWO_ZERO_FOUR_SEVEN) {
            accuracy = ZERO_SIX;
        } else if (size < THREE_TWO_SEVEN_FIVE) {
            accuracy = ZERO_FOUR_FOUR;
        } else {
            accuracy = ZERO_FOUR;
        }
        return accuracy;
    }

如果图片是存在本地的话,想在项目里访问还配置静态资源路径。

WebMvcConfig 代码

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
    //匹配url 中的资源映射
    @Value("${accessFile.resourceHandler}")
    private String resourceHandler;
    //上传文件保存的本地目录
    @Value("${accessFile.location}")
    private String location;

    /**
     * 配置静态资源映射
     *
     * @param registry
     */
    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        //匹配到resourceHandler,将URL映射至location,也就是本地文件夹
        registry.addResourceHandler(resourceHandler).addResourceLocations("file:" + location);
    }


    /**
     * 跨域
     * @param registry
     */
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**")
                .allowedOriginPatterns("*")
                .allowCredentials(true)
                .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")
                .maxAge(3600);
    }
    }

如果项目里有shiro 的话记得一定要放行。不然会一直报无权限的

 @Bean("shiroFilter")
    public ShiroFilterFactoryBean shirFilter(SecurityManager securityManager) {
        ShiroFilterFactoryBean shiroFilter = new ShiroFilterFactoryBean();
        shiroFilter.setSecurityManager(securityManager);

        //oauth过滤
        Map<String, Filter> filters = new HashMap<>();
        filters.put("oauth2", new Oauth2Filter());
        shiroFilter.setFilters(filters);

        Map<String, String> filterMap = new LinkedHashMap<>();
        filterMap.put("/imgs/**", "anon");//放行静态资源路径
        filterMap.put("/**", "oauth2");
        shiroFilter.setFilterChainDefinitionMap(filterMap);
        return shiroFilter;
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值