Java后端如何进行文件上传和下载 —— 本地版

简介: 本文详细介绍了在Java后端进行文件上传和下载的实现方法,包括文件上传保存到本地的完整流程、文件下载的代码实现,以及如何处理文件预览、下载大小限制和运行失败的问题,并提供了完整的代码示例。

大体思路

1、文件上传

文件上传保存到本地,我们要关注的是文件怎样接收,怎样保存,保存在哪?

首先,既然是文件,就要有对应的文件保存地址,或者说文件保存路径和文件保存目录都可以,如下面这个代码,我们定义一个字符串用来表示文件保存地址。

System.getProperty("user.dir") 表示当前后端项目的路径,是固定的写法。它会自动识别当前项目所在的根路径,每个人的可能都不一样

File.separator 表示分隔符,也就是斜杠 /

files表示之后所有的文件都存储在 files 文件包下

比如此处我的 ROOT_PATH(文件路径) 是 D:\code_github\Dream_java\java_chatroom\files

首先,此处每个人的路径肯定都不相同,不要疑问为什么和我的不一样,因为咱项目所在位置就不一样

其次,你也可以指定其它的路径,这都是开放性的选择

private static final String ROOT_PATH = System.getProperty("user.dir") + File.separator + "files";

我们写一个接口,路径随意,比如我这里的 /uploadceshi

@PostMapping("/uploadceshi")

然后呢,我们写对应的方法,方法要有参数,既然是文件,我们就使用 MultipartFile 类型来进行接收,后续也可以使用它的很多内置函数来进行文件的处理

public String uploadCeshi(MultipartFile file){

}

然后,这样一个基础的接口就写好了,而且已经能接收前端传来的文件了,当前端上传文件后,文件就保存成了我们的 file 参数,接下来就可以对文件进行处理了。

首先,我们要获取文件的原始名称来进行存储,并取得文件的主名称和后缀以供后续使用

String originalFilename = file.getOriginalFilename();  // 文件的原始名称    aaa.png
log.info("文件的原始名称:{}", originalFilename);
String mainName = FileUtil.mainName(originalFilename);  // 文件的主名称    aaa
log.info("文件的原始主名称:{}", mainName);
String extName = FileUtil.extName(originalFilename);  // 文件的扩展名(后缀)    .png
log.info("文件的原始后缀:{}", extName);

log.info 是日志打印的代码,类似于 System.out.println() ,如果 log.info 看不懂的话换成 System.out.println() 也是可以的

还记得我们开始定义的保存文件的父级目录么,也就是 ROOT_PATH,现在我们要保存文件了,既然要保存,我们需要判断这个父级目录是否存在,如果不存在,我们要先创建这个 “父级目录”

// 如果当前文件的父级目录不存在,就创建
if(!FileUtil.exist(ROOT_PATH)){
    FileUtil.mkdir(ROOT_PATH);    // 如果当前文件的父级目录不存在,就创建
}

注意 FileUtil 不要导错包了,此处我使用的是 hutool 的

如果不知道 hutool 是啥,在maven仓库里搜下对应的依赖,导入到 pom.xml 里就可以了

hutool 是个知名的工具包,类似于 lombok

未成功先言败,我们继续判断特殊情况,比如当前上传的文件已经存在了,那么这个时候我就要重命名一个文件

// 如果当前上传的文件已经存在了,那么这个时候我就要重命名一个文件
if(FileUtil.exist(ROOT_PATH + File.separator + originalFilename)){
     originalFilename = System.currentTimeMillis() + "-" + mainName + "." + extName;
     log.info("文件已经存在,重命名后的文件名:{}", originalFilename);
}

特殊情况都处理完了,我们进行文件的存储

File saveFile = new File(ROOT_PATH + File.separator + originalFilename);   // 要保存的文件地址/目录
file.transferTo(saveFile);  // 存储文件到本地的磁盘里面去

最后,我们返回给前端一个URL,也就是后续我们的下载接口地址

// 返回文件的链接,这个链接就是文件的下载地址,这个下载地址就是我的后台提供出来的
String url = "http://" + ip + ":" + port + "/file/download?fileName=" + originalFilename;
log.info("文件的下载地址:{}", url);
return url;

ip和port换成你对应的ip和端口号即可,拼接成字符串,比如我这里返回的url:

http://localhost:8080/file/download?fileName=消息队列设计.pdf

完整上传接口代码如下:

ip、port 以及 ROOT_PATH 是我在类中,这个方法外定义的变量,所以没在下面这段代码里

@PostMapping("/uploadceshi")
    public String uploadCeshi(MultipartFile file) throws IOException {
        String originalFilename = file.getOriginalFilename();  // 文件的原始名称    aaa.png
        log.info("文件的原始名称:{}", originalFilename);
        String mainName = FileUtil.mainName(originalFilename);  // 文件的主名称    aaa
        log.info("文件的原始主名称:{}", mainName);
        String extName = FileUtil.extName(originalFilename);  // 文件的扩展名(后缀)    .png
        log.info("文件的原始后缀:{}", extName);
        System.out.println();

        // 如果当前文件的父级目录不存在,就创建
        if(!FileUtil.exist(ROOT_PATH)){
            FileUtil.mkdir(ROOT_PATH);    // 如果当前文件的父级目录不存在,就创建
        }

        // 如果当前上传的文件已经存在了,那么这个时候我就要重命名一个文件
        if(FileUtil.exist(ROOT_PATH + File.separator + originalFilename)){
            originalFilename = System.currentTimeMillis() + "-" + mainName + "." + extName;
            log.info("文件已经存在,重命名后的文件名:{}", originalFilename);
        }

        File saveFile = new File(ROOT_PATH + File.separator + originalFilename);   // 要保存的文件地址/目录
        file.transferTo(saveFile);  // 存储文件到本地的磁盘里面去
        // 返回文件的链接,这个链接就是文件的下载地址,这个下载地址就是我的后台提供出来的
//        String url = "http://" + ip + ":" + port + "/file/download/" + originalFilename;
        String url = "http://" + ip + ":" + port + "/file/download?fileName=" + originalFilename;
        log.info("文件的下载地址:{}", url);
        return url;
    }

2、文件下载

这个接口代码量少,逻辑清晰,我直接将代码全部放在下面,然后一下子讲述完

这个接口的访问地址就是上传接口返回的url

下载接口有两个参数,fileName接收想要下载的文件名

response.addHeader 等会再讲,先简单讲述下作用,使用第一个 response.addHeader 时,访问url文件直接下载,无法预览,使用第二个 response.addHeader 时,访问url文件如果可以预览,则先预览,不可以,会进行下载

先取得完整的文件路径名,如果路径不存在,直接返回空,存在则以字节流数组的方式返回前端

有人可能会疑问,这里我写的返回类型不是 void 么?怎么还可以返回数据给前端呢。这个简单理解为特殊情况吧,而且文件IO本就相对于文本数据的操作有极大的不同

    @GetMapping("/download")
    public void download(String fileName, HttpServletResponse response) throws IOException {
//        response.addHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8"));  // 附件下载
        // 默认格式就是预览,浏览器会根据格式进行判断,如果可以就预览,不可以就下载
//        response.addHeader("Content-Disposition", "inline;filename=" + URLEncoder.encode(fileName, "UTF-8"));  // 附件预览
        String filePath = ROOT_PATH + File.separator + fileName;
        if(!FileUtil.exist(filePath)){
            return;
        }
        byte[] bytes = FileUtil.readBytes(filePath);
        ServletOutputStream outputStream = response.getOutputStream();
        outputStream.write(bytes);    // 数组是一个字节数组,也就是文件的字节流数组
        outputStream.flush();
        outputStream.close();
    }

特殊讲解 —— 必看

1、文件预览/下载

//  response.addHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8"));  // 附件下载
// 默认格式就是预览,浏览器会根据格式进行判断,如果可以就预览,不可以就下载
//  response.addHeader("Content-Disposition", "inline;filename=" + URLEncoder.encode(fileName, "UTF-8"));  // 附件预览

注意这两行代码

  • 使用第一行代码就是文件下载
  • 使用第二行代码就是文件预览,若无法预览则下载(像图片、PDF可以预览,应用软件包等无法1预览)

很多东西可能有疑问?为什么?

那么此处就要讲一下响应中的一个属性了,Content-Disposition,当这个属性默认是inline

  • 当它是 inline 时,浏览器会进行下载操作
  • 当它是 attachment 时,浏览器会进行下载操作

至于详细的就要剖析HTTP或HTTPS的请求和响应格式了,感兴趣的朋友可以自己去了解

2、文件上传/下载大小限制

# 设置上传文件的限制大小
spring:
  servlet:
    multipart:
      max-file-size: 30MB
      max-request-size: 30MB

代码运行失败解决方法

1、包一定不要引错!!!比如 lombok 和 hutool

2、ip和端口号换成自己的,或者像我一样在yml里自己定义

3、文件可以预览或者下载,请详细阅读此篇博客目录中的 “特殊讲解 —— 必看”

4、文件过大无法上传或下载,请详细阅读此篇博客目录中的 “特殊讲解 —— 必看”

完整代码

注:hutool、lombok等自行导入,在maven仓库搜依赖即可(方式很多)

ip、port是我在yml里定义的,你直接换成你自己的ip和端口号即可(一定要换

package com.example.demo.controller;

import cn.hutool.core.io.FileUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;

@RestController
@Slf4j
@RequestMapping("/file")
public class FileCeshiController {

    // 项目启动的ip地址
    @Value("${ip:localhost}")  // 给 ip 一个默认值,防止忘定义时报错
    String ip;

    // 项目启动的端口号
    @Value("${server.port}")
    String port;

    // System.getProperty("user.dir") 获取当前项目的根路径  此处为 D:\code_github\Dream_java\java_chatroom
    // File.separator 分隔符,即 \     (Windows 和 ios 通用)
    private static final String ROOT_PATH = System.getProperty("user.dir") + File.separator + "files";

    @PostMapping("/uploadceshi")
    public String uploadCeshi(MultipartFile file) throws IOException {
        String originalFilename = file.getOriginalFilename();  // 文件的原始名称    aaa.png
        log.info("文件的原始名称:{}", originalFilename);
        String mainName = FileUtil.mainName(originalFilename);  // 文件的主名称    aaa
        log.info("文件的原始主名称:{}", mainName);
        String extName = FileUtil.extName(originalFilename);  // 文件的扩展名(后缀)    .png
        log.info("文件的原始后缀:{}", extName);
        System.out.println();

        // 如果当前文件的父级目录不存在,就创建
        if(!FileUtil.exist(ROOT_PATH)){
            FileUtil.mkdir(ROOT_PATH);    // 如果当前文件的父级目录不存在,就创建
        }

        // 如果当前上传的文件已经存在了,那么这个时候我就要重命名一个文件
        if(FileUtil.exist(ROOT_PATH + File.separator + originalFilename)){
            originalFilename = System.currentTimeMillis() + "-" + mainName + "." + extName;
            log.info("文件已经存在,重命名后的文件名:{}", originalFilename);
        }

        File saveFile = new File(ROOT_PATH + File.separator + originalFilename);   // 要保存的文件地址/目录
        file.transferTo(saveFile);  // 存储文件到本地的磁盘里面去
        // 返回文件的链接,这个链接就是文件的下载地址,这个下载地址就是我的后台提供出来的
        String url = "http://" + ip + ":" + port + "/file/download?fileName=" + originalFilename;
        log.info("文件的下载地址:{}", url);
        return url;
    }

    @GetMapping("/download")
    public void download(String fileName, HttpServletResponse response) throws IOException {
//        response.addHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8"));  // 附件下载
        // 默认格式就是预览,浏览器会根据格式进行判断,如果可以就预览,不可以就下载
//        response.addHeader("Content-Disposition", "inline;filename=" + URLEncoder.encode(fileName, "UTF-8"));  // 附件预览
        String filePath = ROOT_PATH + File.separator + fileName;
        if(!FileUtil.exist(filePath)){
            return;
        }
        byte[] bytes = FileUtil.readBytes(filePath);
        ServletOutputStream outputStream = response.getOutputStream();
        outputStream.write(bytes);    // 数组是一个字节数组,也就是文件的字节流数组
        outputStream.flush();
        outputStream.close();
    }


}

转载至:Java后端如何进行文件上传和下载 —— 本地版(文末配绝对能用的源码,超详细,超好用,一看就懂,博主在线解答) 文件如何预览和下载?(超简单教程)-阿里云开发者社区

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/924499.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Z2400024基于Java+SSM+mysql+maven开发的社区论坛系统的设计与实现(附源码 配置 文档)

基于SSM开发的社区论坛系统 1.摘要2.主要功能3.系统运行环境4.项目技术5.系统界面截图6.源码获取 1.摘要 本文介绍了一个基于SSM(Spring、Spring MVC、MyBatis)框架开发的社区论坛系统。该系统旨在打造一个高品质的开发者社区,为开发者提供一…

JAVA笔记 | 策略模式+枚举Enum简单实现策略模式(可直接套用)

本篇为更为简单的策略模式应用,使用枚举来进行策略分配 上一篇(链接如下)更像是策略工厂模式来分配策略 JAVA笔记 | 实际上用到的策略模式(可直接套用)-CSDN博客 先创建策略相关类 //策略类 public interface PetStrategy {/*** 执行动作 - 跑RUN*/String run(Str…

RabbitMQ 篇-深入了解延迟消息、MQ 可靠性(生产者可靠性、MQ 可靠性、消费者可靠性)

??博客主页:【_-CSDN博客】** 感谢大家点赞??收藏评论** 文章目录 ???1.0 RabbitMQ 的可靠性 ? ? ? ? 2.0 发送者的可靠性 ? ? ? ? 2.1 生产者重试机制 ? ? ? ? 2.2 生产者确认机制 ? ? ? ? 2.2.1 开启生产者确认机制 ? ? ? ? 2.2…

Redis(概念、IO模型、多路选择算法、安装和启停)

一、概念 关系型数据库是典型的行存储数据库,存在的问题是,按行存储的数据在物理层面占用的是连续存储空间,不适合海量数据存储。 Redis在生产中使用的最多的是用作数据缓存。 服务器先在缓存中查询数据,查到则返回,…

JAVA:Spring Boot 3 实现 Gzip 压缩优化的技术指南

1、简述 随着 Web 应用的用户量和数据量增加,网络带宽和页面加载速度逐渐成为瓶颈。为了减少数据传输量,提高用户体验,我们可以使用 Gzip 压缩 HTTP 响应。本文将介绍如何在 Spring Boot 3 中实现 Gzip 压缩优化。 2、配置 Spring Boot 3 对…

python期末复习

其他复习资料 Python期末复习-系列数据类型-CSDN博客 期末python复习-异常和函数-CSDN博客 期末Python复习-输入输出-CSDN博客 目录 一、面向对象程序设计 1.思维导图 2.基本概念 3.类对象和实例对象 3.1创建对象 3.2定义类中的成员变量 3.3类中属性的公有和私有 3.…

HDU Go Running(最小点覆盖 + 网络流优化)

题目大意:有一条无限长跑道,每个人可以规定自己跑步的方向,起点,跑步起止时间。每个人跑步的速度都是1m/s。最后从监控人员哪里得到了n个报告,每个报告给出了某人在某一时候所在的位置,问跑步的最少可能人数…

《用Python实现3D动态旋转爱心模型》

简介 如果二维的爱心图案已经无法满足你的创意,那今天的内容一定适合你!通过Python和matplotlib库,我们可以实现一个动态旋转的3D爱心模型,充满立体感和动感。# 实现代码(完整代码底部名片私信) 以下是完…

Unity-Lightmap入门篇

::这是一个实战文章,并没有知识分享,或理论知识;完全没有 关键字: “lightmap","全局光照”,“light Probe" (会混合一些中英文搜索,或者全英文搜索) …

ElasticSearch通过es-head插件安装可视化及相关问题

1.es-head下载地址 GitHub - mobz/elasticsearch-head: A web front end for an elastic search cluster 2.启动 建议使用vscode启动,并安装好node.js环境 npm installnpm run start 通过http://localhost:9100就可以看到本地添加的es库 3.相关问题 3.1跨域问…

Android PMS(Package Manager Service)源码介绍

文章目录 前言一、PMS 启动流程二、APK 安装流程三、APK 卸载流程四、权限管理静态权限动态权限 五、 数据存储与一致性六、 PMS 的安全性策略1、权限检查2、签名认证3、动态权限管理4、应用安装验证5、保护系统目录 七、PMS 调试方法总结 前言 PackageManagerService&#xf…

OSPTrack:一个包含多个生态系统中软件包执行时生成的静态和动态特征的标记数据集,用于识别开源软件中的恶意行为。

2024-11-22 ,由格拉斯哥大学创建的OSPTrack数据集,目的是通过捕获在隔离环境中执行包和库时生成的特征,包括静态和动态特征,来识别开源软件(OSS)中的恶意指标,特别是在源代码访问受限时&#xf…

Web登录页面设计

记录第一个前端界面,暑假期间写的,用了Lottie动画和canvas标签做动画,登录和注册也连接了数据库。 图片是从网上找的,如有侵权私信我删除,谢谢啦~

MySQL45讲 第29讲 如何判断一个数据库是不是出问题了?——阅读总结

文章目录 MySQL45讲 第二十九讲 如何判断一个数据库是不是出问题了?——阅读总结一、检测数据库实例健康状态的重要性二、常见检测方法及问题分析(一)select 1 判断法(二)查表判断法(三)更新判断…

mac下Gpt Chrome升级成GptBrowser书签和保存的密码恢复

cd /Users/自己的用户名/Library/Application\ Support/ 目录下有 GPT\ Chrome/ Google/ GptBrowser/ GPT\ Chrome 为原来的chrome浏览器的文件存储目录. GptBrowser 为升级后chrome浏览器存储目录 书签所在的文件 Bookmarks 登录账号Login 相关的文件 拷贝到GptBrow…

论文阅读笔记 | EEG:运动执行过程中的ERD

参考:https://mp.weixin.qq.com/s/RmcPSLv1ITMZZwqe2uZ_og?token1093147649&langzh_CN

Android U ART young cc流程分析

概述: 众所周知jvm虚拟机为了提高内存回收效率,更高效的进行内存管理与回收,对堆内存进行了分代管理比如hotspot虚拟机的新生代,老年代。根据各代的特征( 新生代对象分配频繁而生存周期短,老年代生存周期长…

C++ 11重点总结1

智能指针 智能指针: C11引入了四种智能指针: auto_ptr(已弃用)、unique_ptr、shared_ptr和weak_ptr。智能指针可以更有效地管理堆内存,并避免常见的内存泄漏问题。 shared_ptr: 自定义删除器。 shared_ptr使用引用计数来管理它指向的对象的生命周期。多个shared_ptr实例可以指向…

Sickos1.1 详细靶机思路 实操笔记

Sickos1.1 详细靶机思路 实操笔记 免责声明 本博客提供的所有信息仅供学习和研究目的,旨在提高读者的网络安全意识和技术能力。请在合法合规的前提下使用本文中提供的任何技术、方法或工具。如果您选择使用本博客中的任何信息进行非法活动,您将独自承担…

GB28181系列二:SIP信令

我的音视频/流媒体开源项目(github) GB28181系列目录 目录 一、SIP报文介绍 二、SIP交互流程: 1、Session Model 2、Pager Model 3、SIP信令交互过程中的3个定义 三、媒体传输(SDP和RTP) 一、SIP报文介绍 这里将会介绍SIP…