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

news2024/11/29 18:30:48

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

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

SqlServer强制转换函数TRY_CONVERT和TRY_CAST

SqlServer强制转换函数TRY_CONVERT和TRY_CAST的介绍和案例分享 1、本节内容 CAST 和 CONVERT TRY_CAST TRY_CONVERT 适用于: SQL ServerAzure SQL 数据库Azure SQL 托管实例Azure Synapse Analytics 分析平台系统 (PDW)Microsoft Fabric 中的 SQL 分析端点Micro…

透视投影(Perspective projection)与等距圆柱投影(Equirectangular projection)

一、透视投影 1.方法概述 Perspective projection(透视投影)是一种模拟人眼观察三维空间物体时的视觉效果的投影方法。它通过模拟观察者从一个特定视点观察三维场景的方式来创建二维图像。在透视投影中,远处的物体看起来比近处的物体小&…

linux 中后端jar包启动不起来怎么回事 -bash: java: 未找到命令

一、用以下命令检查jdk版本 输入:java -version,如果JDK 环境变量没有配置,你会看到如下提示 二、配置jdk环境 1.先找到/etc/profile文件,然后在该文件最后面加上以下配置 export JAVA_HOME/usr/local/jdk-21.0.1 export PATH$…

TDengine在debian安装

参考官网文档&#xff1a; 官网安装文档链接 从列表中下载获得 Deb 安装包&#xff1b; TDengine-server-3.3.4.3-Linux-x64.deb (61 M) 进入到安装包所在目录&#xff0c;执行如下的安装命令&#xff1a; sudo dpkg -i TDengine-server-<version>-Linux-x64.debNOTE 当…

Java开发工程师最新面试题库系列——Java基础部分(附答案)

如果你有更好的想法请在评论区留下您的答案&#xff0c;一起交流讨论# 面向对象有哪些特征&#xff1f; 答&#xff1a;继承、封装、多态 JDK与JRE的区别是什么&#xff1f; 答&#xff1a;JDK是java开发时所需环境&#xff0c;它包含了Java开发时需要用到的API&#xff0c;JRE…

linux系统下如何将xz及ISO\img等格式压缩包(系统)烧写到优盘(TF卡)

最近用树莓派做了个NAS&#xff0c;效果一般&#xff0c;缺少监控及UI等&#xff0c;详细见这篇文章&#xff1a; https://blog.csdn.net/bugsycrack/article/details/135344782?spm1001.2014.3001.5501 所以下载了专门的基于树莓派的NAS系统直接使用。这篇文章是顺便复习一…

计算机的错误计算(一百六十八)

摘要 算式“(5^25*(1/25)^(1/5)*3^25(1/25)^(1/5)*5^25*3^(251/5)-(9/25)^(1/5)*3^25*5^25-(1/25)^(1/5)*3^25*5.0^25*(13^(1/5)-3^(2/5.0)))”的值为0。但是&#xff0c;在 MATLAB 中计算它&#xff0c;则输出含有15位整数。 例1. 计算 直接贴图吧&#xff1a; 这样&#x…

Python学习------第十四天

匿名函数 1.函数作为参数来传递 &#xff08;函数的参数中调用另外一个函数&#xff09; #定义一个函数&#xff0c;接受另一个函数作为参数传入 #计算逻辑的传入 def test_func(compute):result compute(1,2)print(result)print(f"{type(result)}")print(f"…

【经典论文阅读】Transformer(多头注意力 编码器-解码器)

Transformer attention is all you need 摘要 完全舍弃循环 recurrence 和卷积 convolutions 只依赖于attention mechanisms 【1】Introduction 完全通过注意力机制&#xff0c;draw global dependencies between input and output 【2】Background 1&#xff1a;self-…

Java——多线程案例

目录 一、单例模式 1.饿汉模式 2.懒汉模式 3.线程安全问题 4.解决线程安全问题(懒汉模式) 二、阻塞式队列 1.什么是阻塞队列 2.生产者消费模型 生产者消费者模型意义&#xff1a; 1.解耦合 2.削峰填谷 3.标准库中的阻塞队列 三、定时器 1.定时器是什么 2.标准库中的…

AI 声音:数字音频、语音识别、TTS 简介与使用示例

在现代 AI 技术的推动下&#xff0c;声音处理领域取得了巨大进展。从语音识别&#xff08;ASR&#xff09;到文本转语音&#xff08;TTS&#xff09;&#xff0c;再到个性化声音克隆&#xff0c;这些技术已经深入到我们的日常生活中&#xff1a;语音助手、自动字幕生成、语音导…

Linux服务器安装mongodb

因为项目需要做评论功能&#xff0c;领导要求使用mongodb&#xff0c;所以趁机多学习一下。 在服务器我们使用docker安装mongodb 1、拉取mongodb镜像 docker pull mongo &#xff08;默认拉取最新的镜像&#xff09; 如果你想指定版本可以这样 docker pull mongo:4.4&#…

Java基础 设计模式——针对实习面试

目录 Java基础 设计模式单例模式工厂模式观察者模式策略模式装饰器模式其他设计模式 Java基础 设计模式 单例模式 单例模式&#xff08;Singleton Pattern&#xff09; 定义&#xff1a;确保一个类只有一个实例&#xff0c;并提供一个全局访问点来访问这个实例。适用场景&…

分布式搜索引擎之elasticsearch单机部署与测试

分布式搜索引擎之elasticsearch单机部署与测试 1.部署单点es 1.1.创建网络 因为我们还需要部署kibana容器&#xff0c;因此需要让es和kibana容器互联。这里先创建一个网络&#xff1a; docker network create es-net1.2.加载镜像 这里我们采用elasticsearch的7.12.1版本的…

【工具】JS解析XML并且转为json对象

【工具】JS解析XML并且转为json对象 <?xml version1.0 encodingGB2312?> <root><head><transcode>hhhhhhh</transcode></head><body><param>ccccccc</param><param>aaaaaaa</param><param>qqqq<…

CSDN设置成黑色背景(谷歌 Edge)

一.谷歌浏览器 浏览器地址输入&#xff1a;Chrome://flags搜索框输入&#xff1a;enable-force-dark将default 改成 enabled&#xff0c;点击重启浏览器 二.Edge浏览器 浏览器地址输入&#xff1a;edge://flags搜索里面输入Auto Dark Mode for Web Contents将default 改成 e…

【动手学电机驱动】STM32-FOC(8)MCSDK Profiler 电机参数辨识

STM32-FOC&#xff08;1&#xff09;STM32 电机控制的软件开发环境 STM32-FOC&#xff08;2&#xff09;STM32 导入和创建项目 STM32-FOC&#xff08;3&#xff09;STM32 三路互补 PWM 输出 STM32-FOC&#xff08;4&#xff09;IHM03 电机控制套件介绍 STM32-FOC&#xff08;5&…

OGRE 3D----3. OGRE绘制自定义模型

在使用OGRE进行开发时,绘制自定义模型是一个常见的需求。本文将介绍如何使用OGRE的ManualObject类来创建和绘制自定义模型。通过ManualObject,开发者可以直接定义顶点、法线、纹理坐标等,从而灵活地构建各种复杂的几何体。 Ogre::ManualObject 是 Ogre3D 引擎中的一个类,用…

【网络安全 | 漏洞挖掘】绕过SAML认证获得管理员面板访问权限

未经许可,不得转载。 文章目录 什么是SAML认证?SAML是如何工作的?SAML响应结构漏洞结果什么是SAML认证? SAML(安全断言标记语言)用于单点登录(SSO)。它是一种功能,允许用户在多个服务之间切换时无需多次登录。例如,如果你已经登录了facebook.com,就不需要再次输入凭…

AI自动化剪辑工具:可将长视频中精彩部分提取合成短视频

最近&#xff0c;我发现了一款特别适合当下短视频潮流的自动化工具&#xff0c;它能够让我们轻松从长视频中剪辑出精彩片段&#xff0c;并快速生成适合分享的短视频。 这款工具叫 AI Youtube Shorts Generator&#xff0c;是一个开源项目&#xff0c;特别适合那些喜欢制作短视…