文件上传、下载封装

文件上传、下载封装

FileUtils
package com.test.common.utils;


import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.test.common.utils.file.FileType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.Assert;
import org.springframework.web.multipart.MultipartFile;

import java.io.*;
import java.nio.ByteBuffer;
import java.nio.channels.Channels;
import java.nio.channels.FileChannel;
import java.nio.channels.ReadableByteChannel;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.UUID;

public abstract class FileUtils {
    
    private static final Logger log = LoggerFactory.getLogger(FileUtils.class);

    /**
     * @description  文件上传
     * @param uploadFile 上传的文件
     * @param dirPath 文件要放在哪个目录下
     * @param createFlag 如果dirPath不存在是否创建
     * */
    public static String upload(String dirPath, MultipartFile uploadFile, boolean createFlag) throws IOException {
        Assert.isTrue(StrUtil.isNotEmpty(dirPath), "dirPath不能为空");
        Assert.isTrue(ObjectUtil.isNotNull(uploadFile), "file不能为空");

        // 校验文件是否符合规范
        String originalFilename = uploadFile.getOriginalFilename();
        String[] strs = originalFilename.split("\\.");
        String suffix = strs[strs.length - 1];
        FileType fileType = FileType.getBySuffixName("."  + suffix);

        if(!isNormative(fileType, uploadFile.getBytes())) {
            throw new IllegalArgumentException(String.format("文件内容与文件类型不匹配"));
        }

        String chineseChar = hasChinese(dirPath);
        Assert.isTrue(StrUtil.isEmpty(chineseChar),String.format("dirPath包含中文字符[%s]",chineseChar));

        final String tempPath = dirPath;

        String osName = SystemUtils.judgeSystem();

        // 校验dirPath中是否包含不属于当前操作系统的路径分隔符
        SystemUtils.SystemType[] systemTypes = SystemUtils.SystemType.values();
        boolean matchResult = Arrays.stream(systemTypes).filter(systemType -> !systemType.getTypeName().equalsIgnoreCase(osName))
            .anyMatch(systemType -> tempPath.contains(systemType.getFileSeparator()));
        if(matchResult) {
            throw new IllegalArgumentException("dirPath中包含不属于当前操作系统的路径分隔符");
        }

        dirPath = dirPath + (dirPath.endsWith(File.separator) ? "" : File.separator);

        File file = new File(dirPath);
        boolean isExists = file.exists();

        if(isExists) {
            if(!file.isDirectory()) {
                throw new IllegalArgumentException(String.format("存在%s,但是类型不是目录",dirPath));
            }
        } else {
            if(!createFlag) {
                throw new IllegalArgumentException(String.format("目标路径[%s]不存在",dirPath));
            }
            // 创建目录
            boolean mkResult = file.mkdirs();
            if(!mkResult) {
                throw new RuntimeException(String.format("目录[%s]创建失败",dirPath));
            }
        }

        String newFileName = "TEST_UPLOAD" + originalFilename.substring(0,originalFilename.lastIndexOf(46))  + DateUtil.format(new Date(), "yyyy-MM-dd") + UUID.randomUUID();
        
        String newFileFullName = dirPath + newFileName + "." + suffix ;

        ByteBuffer buffer = ByteBuffer.allocate(10240);
        ReadableByteChannel readChannel = Channels.newChannel(uploadFile.getInputStream());
        File newFile = new File(newFileFullName);
        if(!newFile.exists()) {
            if(!newFile.createNewFile()) {
                throw new RuntimeException(String.format("文件[%s]创建失败"));
            }
        }
        FileChannel writeChannel = new FileOutputStream(newFile).getChannel();
        while (readChannel.read(buffer) != -1) {
            buffer.flip();
            writeChannel.write(buffer);
            buffer.clear();
        }
        writeChannel.close();
        readChannel.close();

        return newFileFullName;
    }

     /**
     * @description 文件下载
     * @param fileFullName 文件全路径名称
     * @param fileName 浏览器下载的文件名称
     * @param response
     * */
    public void download(@Nullable String fileName, @NotNull String fileFullName,@NotNull HttpServletResponse response) {
        Assert.isTrue(ObjectUtil.isNotNull(response),"response不能为空");
        Assert.isTrue(StrUtil.isNotEmpty(fileFullName),"fileFullName不能为空");

        SystemUtils.SystemType currentSystem = SystemUtils.getCurrentSystem();

        // 若fileName为空, 则取fileFullName的最后一部分作为下载时的文件名
        if(StrUtil.isEmpty(fileName)) {
            String[] strs = fileFullName.split(currentSystem.getFileSplitter());
            fileName = strs[strs.length-1];
        }

        // 设置响应头和浏览器保存文件名
        response.setCharacterEncoding("utf-8");
        response.setContentType("multipart/form-data");
        response.setHeader("Content-Disposition", "attachment;fileName=" + fileName);

        File targetFile = new File(fileFullName);
        if(!targetFile.exists()) {
            throw new IllegalStateException(String.format("文件%s不存在",fileFullName));
        }
        if(!targetFile.isFile()) {
            throw new IllegalArgumentException(String.format("文件%s不是文件",fileFullName));
        }

        try {
            ByteBuffer buffer = ByteBuffer.allocate(10240);
            FileChannel readChannel = new FileInputStream(targetFile).getChannel();
            BufferedOutputStream outputStream = new BufferedOutputStream(response.getOutputStream());

            while (readChannel.read(buffer) != -1) {
                buffer.flip();
                outputStream.write(buffer.array());
                outputStream.flush();
                buffer.clear();
            }

            outputStream.close();
            readChannel.close();

        } catch (FileNotFoundException e) {
            throw new RuntimeException(e);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }

    }


    /**
     * 校验文件是否符合魔法数规范
     * @param fileType
     * @param bytes
     * */
    public static boolean isNormative(FileType fileType, byte[] bytes) {
        List<String> magicNumbers = fileType.getMagicNumbers();
        int[] magicNumberLength = fileType.getMagicNumberLength();
        for(int i = 0;i < magicNumberLength.length; i++) {
            int contentLength = magicNumberLength[i];
            byte[] content = new byte[contentLength];
            for(int j = 0;j < contentLength;j++) {
                content[j] = bytes[j];
            }
            StringBuilder stringBuilder = new StringBuilder();
            for(int j=0;j<content.length;j++) {
                int val = content[j] & 0XFF;
                String hexVal = Integer.toHexString(val);
                if(hexVal.length() < 2) {
                    stringBuilder.append("0");
                }
                stringBuilder.append(hexVal);
            }
            if(!stringBuilder.toString().equalsIgnoreCase(magicNumbers.get(i))) {
                return false;
            }
        }
        return true;
    }


    /**
     * @description 校验是否存在中文
     * @param path 路径
     * @return 中文字符
     * */
    public static String hasChinese(String path) {
        Assert.isTrue(StrUtil.isNotEmpty(path),"path不能为空");
        char[] charArr = path.toCharArray();
        for(int i = 0; i<charArr.length; i++) {
            char val = charArr[i];
            if(((int)val) > 127) {
                return String.valueOf(val);
            }
        }
        return null;
    }

}
FileType
package com.test.common.utils.file;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import org.springframework.util.Assert;

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public enum FileType {

    /**
     * JPEG
     * */
    JPEG("JPEG", Stream.of("FFD8FF").collect(Collectors.toList()), new int[]{3},Stream.of(".jpg",".jpeg",".jpe",".jfif",".exif").collect(Collectors.toList())),

    /**
     * PNG
     * */
    PNG("PNG",Stream.of("89504E470D0A1A0A").collect(Collectors.toList()), new int[]{8},Stream.of(".png").collect(Collectors.toList())),

    /**
     * GIF
     * */
    GIF("GIF",Stream.of("474946383961","4749383761").collect(Collectors.toList()), new int[]{6,6},Stream.of(".gif").collect(Collectors.toList())),

    /**
     * BMP
     * */
    BMP("BMP",Stream.of("424D").collect(Collectors.toList()), new int[]{2},Stream.of(".bmp",".dib").collect(Collectors.toList())),

    /**
     * TIFF
     * */
    TIFF("TIFF",Arrays.asList("4D4D002A","49492A00"),new int[]{4,4},Arrays.asList(".tif",".tiff")),

    /**
     * WORD
     * */
    WORD("WORD",Arrays.asList("D0CF11E0A1B11AE1"),new int[]{8},Arrays.asList(".doc")),

    /**
     * WORD_XML
     * */
    WORD_XML("WORD_XML",Arrays.asList("504B0304"),new int[]{4},Arrays.asList(".docx",".docm",".dotx",".dotm")),

	/**
   * PDF
  **/
    PDF("PDF",Arrays.asList("25504446"),new int[]{4},Arrays.asList(".pdf")),

    /**
     * PPT
     * */
    PPT("PPT",Arrays.asList("D0CF11E0A1B11AE1"),new int[]{8},Arrays.asList(".ppt")),

    /**
     * PPT_XML
     * */
    PPT_XML("PPT_XML",Arrays.asList("504B0304"),new int[]{4},Arrays.asList(".pptx",".pptm",".potx",".potm")),

    /**
     * EXCEL
     * */
    EXCEL("EXCEL",Arrays.asList("D0CF11E0A1B11AE1"),new int[]{8},Arrays.asList(".xls")),

    /**
     * EXCEL_XML
     * */
    EXCEL_XML("EXCEL_XML",Arrays.asList("504B030414000600"),new int[]{8},Arrays.asList(".xlsx",".xlsm",".xlsb",".xltx")),

    /**
     * EXCEL_XLT
     * */
    EXCEL_XLT("EXCEL_XLT",Arrays.asList("D0CF11E0A1B11AE1"),new int[]{8},Arrays.asList(".xlt")),

    /**
     * TXT
     * */
    TXT("TXT",Arrays.asList(".txt",".text"));




    // 文件类型
    private String fileType;

    // 文件类型所有的魔法数
    private List<String> magicNumbers;

    // 魔法数的字节长度
    private int[] magicNumberLength;

    // 文件类型所有的后缀名
    private List<String> suffixNames;

    FileType(String fileType, List<String> magicNumbers, int[] magicNumberLength, List<String> suffixNames) {
        Assert.isTrue(StrUtil.isNotEmpty(fileType),"fileType不能为空");
        Assert.isTrue(CollUtil.isNotEmpty(magicNumbers),"magicNumbers不能为空");
        Assert.isTrue(null != magicNumberLength && magicNumberLength.length > 0 && magicNumberLength.length == magicNumbers.size(),
            "magicNumberLength不能为空且长度必须等于magicNumbers的个数");
        Assert.isTrue(CollUtil.isNotEmpty(suffixNames),"suffixNames不能为空");
        this.fileType = fileType;
        this.magicNumbers = magicNumbers;
        this.magicNumberLength = magicNumberLength;
        this.suffixNames = suffixNames;
    }

    FileType(String fileType, List<String> suffixNames) {
        Assert.isTrue(StrUtil.isNotEmpty(fileType),"fileType不能为空");
        Assert.isTrue(CollUtil.isNotEmpty(suffixNames),"suffixNames不能为空");
        this.fileType = fileType;
        this.suffixNames = suffixNames;
    }


    public String getFileType() {
        return fileType;
    }

    public List<String> getMagicNumbers() {
        return magicNumbers;
    }

    public int[] getMagicNumberLength() {
        return magicNumberLength;
    }

    public List<String> getSuffixNames() {
        return suffixNames;
    }

    /**
     * 根据文件后缀名获取FileType
     * @param fileSuffixName 文件后缀名
     * */
    public static FileType getBySuffixName(String fileSuffixName) {
        if(!isSupported(fileSuffixName)) {
            throw new IllegalArgumentException(String.format("暂不支持处理该类型的文件[%s]",fileSuffixName));
        }
        FileType[] arr = values();
        List<FileType> fileTypeList = Arrays.stream(arr).filter(val->val.suffixNames.contains(fileSuffixName)).collect(Collectors.toList());
        if(fileTypeList.size() > 1) {
            throw new IllegalArgumentException(String.format("文件类型存在错误[%s]",fileSuffixName));
        }
        return fileTypeList.get(0);
    }


    /**
     * 验证文件类型是否支持
     * @param fileSuffixName 文件后缀名
     * */
    public static boolean isSupported(String fileSuffixName) {
        FileType[] arr = values();
        return Arrays.stream(arr).anyMatch(val->val.suffixNames.contains(fileSuffixName));
    }



}
SystemUtils
package com.test.common.utils;

import cn.hutool.core.util.StrUtil;
import org.springframework.util.Assert;

import java.io.File;
import java.util.Locale;

public abstract class SystemUtils {

    
    /**
     * @description 判断当前的操作系统类型
     * @return 操作系统的名称(linux/windows)
     * */
    public static String judgeSystem() {
        String osName = System.getProperty("os.name").toLowerCase(Locale.ROOT);
        if(osName.contains(SystemType.LINUX.typeName)) {
            return SystemType.LINUX.typeName;
        } else if(osName.contains(SystemType.WINDOWS.typeName)) {
            return SystemType.WINDOWS.typeName;
        } else {
            throw new IllegalArgumentException("系统类型未知");
        }
    }

    /**
     * @description 获取当前的操作系统类型
     * @return SystemType
     * */
    public static SystemType getCurrentSystem() {
        SystemType[] systemTypes = SystemType.values();
        String currentSystemName = judgeSystem();
        return Arrays.stream(systemTypes).filter(systemType -> systemType.typeName.equalsIgnoreCase(currentSystemName)).findFirst().orElseThrow(()-> new RuntimeException("无法获取当前操作系统类型"));
    }

    enum SystemType {

        LINUX("linux", "/", "/"),WINDOWS("windows","\\", "\\\\");

        private String typeName;

        private String fileSeparator;

        private String fileSplitter;

        SystemType(String typeName, String fileSeparator, String fileSplitter) {
            Assert.isTrue(StrUtil.isNotEmpty(typeName),"typeName不能为空");
            Assert.isTrue(StrUtil.isNotEmpty(fileSeparator),"fileSeparator不能为空");
            Assert.isTrue(StrUtil.isNotEmpty(fileSplitter),"fileSplitter不能为空");
            this.typeName = typeName;
            this.fileSeparator = fileSeparator;
            this.fileSplitter = fileSplitter;
        }

        public String getTypeName() {
            return typeName;
        }

        public String getFileSeparator() {
            return fileSeparator;
        }

        public String getFileSplitter() {
            return fileSplitter;
        }
    }




}

  • 8
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值