Springboot文件上传下载

Springboot文件上传下载

文件的上传操作

前端代码

<form action="http://localhost:8091/file" method="post" 
      enctype="multipart/form-data">
    <input name="fileImage" type="file" />
    <input type="submit" value="提交"/>
</form>

服务端controller

@RequestMapping("/upload")
public String file(MultipartFile fileImage) {
    String fileDirPath = "D:/images"; // 存放位置
    File dirFile = new File(fileDirPath); 
    // 判断文件目录是否存在
    if (!dirFile.exists()) {
        // 如果目录没有, 则应该新建目录
        dirFile.mkdir();
    }
    // 准备文件上传路径, 路径 + 文件名称
    String fileName = fileImage.getOriginalFilename(); // 获取文件名
    File realFile = new File(fileDirPath + "/" + fileName); // 保存磁盘路径 + 文件名拼接
    // 将字节信息输出到文件中
    try {
        fileImage.transferTo(realFile); // 保存到指定的路径
        return "文件上传成功";
    } catch (IOException e) {
        e.printStackTrace();
        return "文件上传失败";
    }
}

文件下载操作

下载操作也是非常的简单, 我们只需要用流的形式返回到客户端即可

别看长, 其实没多少代码

@RequestMapping("/download")
@ResponseBody
public String downloadFile(HttpServletResponse response) {
    String fileName = "aaa.txt";// 设置文件名,即用户下载得到文件的名字(可以和磁盘中的文件名不同)
    if (fileName != null) {
        //设置文件路径
        String filePath = "D:/file/001.txt"; // 要下载的文件位置
        File file = new File(filePath);
        if (file.exists()) {
            // 关于: application/octet-stream
            // 1、只能提交二进制,而且只能提交一个二进制,如果提交文件的话,只能提交一个文件,后台接收参数只能有一个,而且只能是流(或者字节数组)
            // 2、属于HTTP规范中Content-Type的一种
            // 3、很少使用
            response.setContentType("application/octet-stream");
            // response.setHeader("content-type", "application/octet-stream"); // 也可以这样写
            response.setHeader("Content-Disposition", "attachment;fileName=" + fileName);// 设置文件名
            byte[] buffer = new byte[1024];
            FileInputStream fis = null;
            BufferedInputStream bis = null;
            try {
                fis = new FileInputStream(file);
                bis = new BufferedInputStream(fis);
                OutputStream os = response.getOutputStream();
                int i = bis.read(buffer);
                while (i != -1) {
                    os.write(buffer, 0, i);
                    i = bis.read(buffer);
                }
                System.out.println("success"); // 下载成功返回信息
            } catch (Exception e) {
                e.printStackTrace();
            } finally { // 关闭流
                if (bis != null) {
                    try {
                        bis.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
                if (fis != null) {
                    try {
                        fis.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }
    return null; // 下载失败返回信息
}

扩展: 上传图片并校验

上传图片是经常有的操作, 所以在上传图片的时候也要对文件进行校验, 防止不符合规范的文件或者伪装的木马病毒等, 例如:

  1. 校验文件的有效性
  2. 校验文件是否为恶意程序
  3. 提高用户简餐图片的效率 分目录存储
  4. 为了防止重名图片的提交 自定义文件名
  5. 实现图片的物理上传
  6. 准备一个访问的虚拟路径

Controller编写

@Autowarld
private FileService fileService;

@RequestMapping("/pic/upload")
public ImageVO upload(MultipartFile uploadFile) {
    return fileService.upload(uploadFile); // 调用service进行保存图片
}

Service编写(接口略)

service类-结构

@Service
public class FileServiceImpl implements FileService {
	@Override
    public ImageVO upload(MultipartFile uploadFile) {
        
    }
}

1.校验文件后缀

service中定义一个localDirPath静态变量, 用于存储支持的图片你后缀, 例如:

private static Set<String> imageTypeSet = new HashSet<>();
static {
    imageTypeSet.add(".jps");
    imageTypeSet.add(".png");
    imageTypeSet.add(".gif");
    imageTypeSet.add("/bmp");
    //.....其他省略
}

upload方法中

// 1. 图片的类型 (1) 利用正则表达式   (2) 利用集合的方式进行校验Set 数据是否存在即可
String fileName = uploadFile.getOriginalFilename();
// 获取后缀 并转为全小写
String fileType = fileName.substring(fileName.lastIndexOf(".")).toLowerCase();
if (!imageTypeSet.contains(fileType)) { // 如果后缀没在集合中, 则上传失败
    return ImageVO.fail();
}

2.检验是否为图片

upload方法中

//  2. 如何判断文件是否为恶意程序, 是否有图片的属性
// 2.1 将上传文件类型利用图片API进行转化
try {
    BufferedImage bufferedImage = ImageIO.read(uploadFile.getInputStream());
    // 2.2 校验是否有图片的特有属性 高度, 宽度
    int width = bufferedImage.getWidth();
    int height = bufferedImage.getHeight();
    // 2.3 校验宽度和高度是否有值
    if (width == 0 || height == 0) {
        return ImageVO.fail();
    }
} catch (IOException e) {
    e.printStackTrace();
    return ImageVO.fail();
}

3.分目录存储

service中定义属性, 表示文件存储的位置根路径

private String localDirPath = "D:/images"; // 定义本地磁盘目录

upload方法中

// 3. 实现分目录存储:
// 方案1: 利用hash之后每隔2-3位截取之后拼接
// 方案2: 以时间为单位进行分割   /yyyy/MM/dd/ 这里使用方案2, 方案1自行脑补
// 3.1 利用工具的API将时间转化为执行的格式
String datePath = new SimpleDateFormat("/yyyy/MM/dd/").format(new Date());
// 3.2 动态生成文件目录 2部分=根目录+时间目录
String localDir = localDirPath + datePath;
// 3.3 判断目录是否存在, 如果不存在则新建目录
File dirFile = new File(localDir);
if (!dirFile.exists()) {
    dirFile.mkdirs(); // 如果不存在, 则新建目录
}

4.自定义文件名称

使用UUID自定义名称

//4. 防止文件重名, 需要自定义文件名称 UUID
//4.1 生成uuid
String uuid = UUID.randomUUID().toString().replace("-", "");
//4.2动态生成文件名称
String uuidFileName = uuid + fileType;

5.保存上传的文件

// 5. 实现文件的上传
String realFilePath = localDir + uuidFileName;
// 5.1 封装文件的真实对象
File imageFile = new File(realFilePath);
// 5.1 实现文件的保存
try {
    uploadFile.transferTo(imageFile);
} catch (IOException e) {
    e.printStackTrace();
    return ImageVO.fail();
}

6.返回虚拟url路径

// 6.暂时使用京东的路径代替
// 检查文件上传业务调用是否正确
// [http://image.jt.com] + [/yyyy/MM/dd/] + [文件名]
String url =  urlPath + datePath + uuidFileName;
return ImageVO.succcess(url);

完整文件如下

@Service
@PropertySource("classpath:/properties/image.properties")
public class FileServiceImpl implements FileService {

    private static Set<String> imageTypeSet = new HashSet<>();
    @Value("${image.localDirPath}")
    private String localDirPath; // 定义本地磁盘目录
    static {
        imageTypeSet.add(".jps");
        imageTypeSet.add(".png");
        imageTypeSet.add(".gif");
        imageTypeSet.add("." +
                "bmp");
        //.....其他省略
    }

    @Value("${image.urlPath}")
    private String urlPath;


    /**
     * 1. 校验文件的有效性   .jsp|.png|.bmp|.gif
     * 2. 校验文件是否为恶意程序
     * 3. 提高用户简餐图片的效率  分目录存储
     * 4. 为了防止重名图片的提交  自定义文件名
     * 5. 实现图片的物理上传
     * 6. 准备一个访问的虚拟路径
     */
    @Override
    public ImageVO upload(MultipartFile uploadFile) {
        // 1. 图片的类型 (1) 利用正则表达式   (2) 利用集合的方式进行校验Set 数据是否存在即可
        String fileName = uploadFile.getOriginalFilename();
        // 获取后缀 并转为全小写
        String fileType = fileName.substring(fileName.lastIndexOf(".")).toLowerCase();
        if (!imageTypeSet.contains(fileType)) {
            return ImageVO.fail();
        }

        //  2. 如何判断文件是否为恶意程序, 是否有图片的属性
        // 2.1 将上传文件类型利用图片API进行转化
        try {
            BufferedImage bufferedImage = ImageIO.read(uploadFile.getInputStream());
            // 2.2 校验是否有图片的特有属性 高度, 宽度
            int width = bufferedImage.getWidth();
            int height = bufferedImage.getHeight();
            // 2.3 校验宽度和高度是否有值
            if (width == 0 || height == 0) {
                return ImageVO.fail();
            }
        } catch (IOException e) {
            e.printStackTrace();
            return ImageVO.fail();
        }

        // 3. 实现分目录存储:
        // 方案1: 利用hash之后每隔2-3位截取之后拼接
        // 方案2: 以时间为单位进行分割   /yyyy/MM/dd/
        // 3.1 利用工具的API将时间转化为执行的格式
        String datePath = new SimpleDateFormat("/yyyy/MM/dd/").format(new Date());
        // 3.2 动态生成文件目录 2部分=根目录+时间目录
        String localDir = localDirPath + datePath;
        // 3.3 判断目录是否存在, 如果不存在则新建目录
        File dirFile = new File(localDir);
        if (!dirFile.exists()) {
            dirFile.mkdirs(); // 如果不存在, 则新建目录
        }

        //4. 防止文件重名, 需要自定义文件名称 UUID
        //4.1 生成uuid
        String uuid = UUID.randomUUID().toString().replace("-", "");
        //4.2动态生成文件名称
        String uuidFileName = uuid + fileType;

        // 5. 实现文件的上传
        String realFilePath = localDir + uuidFileName;
        // 5.1 封装文件的真实对象
        File imageFile = new File(realFilePath);
        // 5.1 实现文件的保存
        try {
            uploadFile.transferTo(imageFile);
        } catch (IOException e) {
            e.printStackTrace();
            return ImageVO.fail();
        }

        // 6.暂时使用京东的路径代替
        // 检查文件上传业务调用是否正确
        // [http://image.jt.com] + [/yyyy/MM/dd/] + [文件名]
        String url =  urlPath + datePath + uuidFileName;
        return ImageVO.succcess(url);
    }
}

页面显示图片

可参考nginx中的图片服务器配置 [点击跳转到nginx图片服务器配置]

posted @ 2020-08-06 16:04  zpk-aaron  阅读(329)  评论(0编辑  收藏  举报