当前位置: 首页 > news >正文

第二篇-进阶-第十四章-上传与下载

Java有很多种上传文件的实现方案,比如使用@MultipartConfig注解、Apache提供的commonupload组件等,SpringBoot采用的更为简单的方案(即org.springframework.web.multipart.MultipartFile接口)。

Java下载文件的实现方式也有很多种,比如静态的URL地址(不推荐)、WebSocket输出流等,SpringBoot通过HttpServletResponse输出流实现下载文件的功能。

14.1上传文件

当上传一个文件时,SpringBoot使用org.springframework.web.multipart.MultipartFile接口,当同时传多个文件,springBoot使用org.springframework.web.multipart.MultipartRequest接口予以实现。

14.1.1只上传一个文件

SpringBoot通过MultipartFile接口实现接收前端文件的功能,MultipartFile接口提供的方法及说明:

返回值

方法

说明

byte[]

getBytes()

返回文件的字节数组形式

String

getContentType()

返回文件的内容类型

InputStream

getInputStream()

获取文件的字节输入流

String

getName()

获取文件所用的参数名

String

getOriginalFilename()

获取上传文件的原始文件名

long

getSize()

返回文件包含的字节数

boolean

isEmpty()

上传的文件是否为空

void

transferTo(File dest)

将接收到的文件转移到dest文件

void

transferTo(Path dest)

将接收的文件转移到dest路径,dest为NIO中的Path接口

服务器如果想要接收前端文件,就需要为控制器方法添加一个MultipartFile类型的参数,该参数会自动获取前端传来的数据,调用MultipartFile接口的transferTo()方法就能将上传文件的数据转移到服务器的本地文件中。

@RestController
public class UploadFileController {private final Logger logger = LoggerFactory.getLogger(UploadFileController.class.getName());@RequestMapping("/file")public String uploadFile(@RequestParam("file") MultipartFile file) {File fileOndesk = new File("E:\\upload\\");if (!fileOndesk.exists()) {logger.info("创建存储文件夹");fileOndesk.mkdirs();}try {String fileName = file.getOriginalFilename();logger.info("保存文件");file.transferTo(new File(fileOndesk, fileName));return "success";} catch (Exception e) {System.out.println("上传出错" + e.getMessage());return "wrong";}}
}

程序运行后使用Postman进行测试

最终发现文件保存到专门的文件夹中

如果上传文件过大,服务器可能会拒绝上传文件的请求,这就可以手动配置服务器允许上传文件的容量上限。(实测上传1个G的文件,报500的服务器错误异常)

application.properties#开启multipart上传功能(设置体积单位为MB) 
spring.servlet.multipart.enabled=true 
#最大文件大小 
spring.servlet.multipart.max-file-size=10MB 
#最大请求大小 
spring.servlet.multipart.max-request-size=215MB

14.1.2 同时上传多个文件

SpringBoot通过org.springframework.web.multipart.MultipartRequest接口提供的方法实现获取批量文件的功能。

MultipartRequest接口提供的方法如下:

返回值

方法

说明

MultipartFile

getFile(String name)

获取指定参数名的上传文件对象,如果不存在此参数,则返回为null

Map

getFileMap()

返回请求中上传文件的参数名的键值对,键为参数名,值为文件对象

Iterator

getFileName()

返回请求中上传文件对应的参数名的迭代器

List

getFiles(String name)

返回此请求中上传文件的内容和说明,不存在则返回空列表

MultiValueMap

getMultiFileMap()

功能同getFileMap(),返回类型为MultiValueMap

String

getMultipartContentType()

返回上传文件的内容类型,图片文件可能为image/jpeg

服务器如果想要批量获取这些文件,就需要在控制器方法中添加HttpServletRequest参数,再将该参数强转为MultipartHttpServletRequest类型,而后调用getFiles()方法。

具体实现如下:

@RestController
public class UploadFileController {private final Logger logger = LoggerFactory.getLogger(UploadFileController.class.getName());@RequestMapping("/files")public String uploadFiles(HttpServletRequest request) throws IOException {MultipartHttpServletRequest mrequest = (MultipartHttpServletRequest) request;List<MultipartFile> fileList = mrequest.getFiles("files");//根据请求参数获取文件列表logger.info("上传文件个数=" + fileList.size());File dir = new File("E:\\upload");for (MultipartFile file : fileList) {if (file.isEmpty()) {continue;}logger.info("上传文件大小=" + file.getSize());String fileName = file.getOriginalFilename();file.transferTo(new File(dir, fileName));}return "success";}}

程序运行后使用Postman进行测试

最终文件成功上传到指定文件夹下。

14.2下载文件

在实际开发中,SpringBoot通过HttpServletResponse输出流实现下载文件的功能。为了获取HttpServletResponse的输出流,需要对服务器响应客户端请求的response对象进行如下设置:

response.setContentType("application/octet-stream"); 
response.addHeader("Content-Disposition","attachment;fileName=demo.png");
  • 当使用HttpServletResponse传输文件时,必须将传输类型设定为"multipart/form-data"或者"application/octet-stream",这样客户端才能知道服务器返回的是一个文件,而不是一堆乱码。
  • Content-Disposition表示下载文件的标识字段,attachment表示在服务器的响应中添加附件,fileName=demo.png标识下载的文件名是demo.png
  • 在设置response对象后,通过response对象调用getOutputStream()方法即可获取HttpServletResponse输出流,使用此输出流就能够实现后端向前端传输文件的功能

具体实现如下:

@Controller
public class DownloadController {Logger logger = LoggerFactory.getLogger(DownloadController.class.getName());@RequestMapping("/download/{code}")public void download(@PathVariable String code, HttpServletResponse response) throws Exception {String path = "";if ("head".equals(code)) {path = "E:\\upload\\head.jpg";} else if ("op1".equals(code)) {path = "E:\\upload\\op1.png";} else if ("op2".equals(code)) {path = "E:\\upload\\op2.png";} else {return;}logger.info("path="+path);File file = new File(path);if (file == null || !file.exists() || file.length() == 0) {logger.info("文件有问题");return;}response.setContentType("application/octet-stream");response.addHeader("Content-Length", String.valueOf(file.length()));//对文件名中的中文进行编码,防止中文乱码String fileName = URLEncoder.encode(file.getName(), "UTF-8");logger.info("fileName=="+fileName);//让文件作为附件被下载response.addHeader("Content-Disposition", "attachment;fileName=" + fileName);OutputStream os = response.getOutputStream();FileInputStream fis = new FileInputStream(file);byte[] buffer = new byte[1024];int len = 0;while ((len = fis.read(buffer)) != -1) {os.write(buffer, 0, len);}fis.close();os.close();}}

运行可下载图片附件

14.3 上传Excel文件中的数据

Excel可以算是简单的关系型存储数据的形式,就可以实现批量化数据上传。SpringBoot推荐使用Apache POI库,简称POI。

14.3.1添加POI依赖

Excel有2种格式,xls和slsx,前者是2003及更早版本的格式,后者是2007及以后的格式,添加的依赖各不相同,分别如下

xls格式的依赖

<dependency> <groupId>org.apache.poi</groupId> <artifactId>poi</artifactId> <version>4.1.2</version> 
</dependency>

xlsx格式的依赖(默认使用这个即可)

<dependency> <groupId>org.apache.poi</groupId> <artifactId>poi-ooxml</artifactId> <version>4.1.2</version> 
</dependency>

14.3.2 读取Excel文件中的数据

POI将一个Excel文件划分下图的几个部分。每一个部分都对应一个POI接口,其中,Workbook表示整个Excel文件,Sheet表示Excel文件中的分页,Row标识Excel文件中的一行,Cell表示Excel文件中某一行的一个具体的单元格。

这些POI接口都位于org.apache.poi.ss.usermodel包下,读取Excel文件的每一个单元格的数据都需要按照"workbook-->Sheet-->Row-->Cell"的顺序创建对应的POI接口对象。

  • 创建Workbook对象,可以通过文件创建,也可以通过字节流来创建Workbook对象
//1,根据File对象获取Workboot对象
File file = new File(exclePath);
Workbook workbook = WorkbookFactory.create(file);//2,根据字节流创建Workboot对象
InputStream is = new FileInputStream(exclePath);
Workbook workbook1 = WorkbookFactory.create(is);
  • 创建Workbook后才能根据位置获取Sheet对象
Sheet sheet = workbook.getSheetAt(i);
  • 得到Sheet对象后才能得到Row对象
Row row = sheet.getRow(0);
  • 得到Row对象后才能根据位置得到精准的Cell对象
Cell cell=row.getCell(i); cell.toString()就能得到具体每个格子内的内容

以学生表test.excel为例

读取excle表中数据,并通过请求返回,程序如下:

@RestController
@RequestMapping("excel")
public class ExcelController {Logger logger = LoggerFactory.getLogger(ExcelController.class.getName());@RequestMapping("/read")public ArrayList<Student> readExcel() throws Exception {String exclePath = "E:\\upload\\test.xlsx";//1,根据File对象获取Workboot对象File file = new File(exclePath);Workbook workbook = WorkbookFactory.create(file);//2,根据字节流创建Workboot对象InputStream is = new FileInputStream(exclePath);Workbook workbook1 = WorkbookFactory.create(is);if (workbook != null) {int sheetNum = workbook.getNumberOfSheets();//得到sheet个数logger.info("sheet个数=" + workbook.getNumberOfSheets());ArrayList<Student> allStudents = new ArrayList<>();for (int i = 0; i < sheetNum; i++) {Sheet sheet = workbook.getSheetAt(i);if (sheet != null) {int rowNum = sheet.getPhysicalNumberOfRows();//得到row个数for (int j = 0; j < rowNum; j++) {Row row = sheet.getRow(j);//得到行对象Rowif (row != null) {int cellNum = row.getPhysicalNumberOfCells();if (cellNum == 4) {Student student = new Student();student.setName(row.getCell(0).toString());//根据索引得到每个精准的单元,并获取内容student.setAge(row.getCell(1).toString());student.setAddress(row.getCell(2).toString());student.setInfo(row.getCell(3).toString());allStudents.add(student);}}}}}return allStudents;} else {logger.info("workbook==null");}return null;}class Student {String name;String age;String address;String info;public String getName() {return name;}public void setName(String name) {this.name = name;}public String getAge() {return age;}public void setAge(String age) {this.age = age;}public String getAddress() {return address;}public void setAddress(String address) {this.address = address;}public String getInfo() {return info;}public void setInfo(String info) {this.info = info;}}@RequestMapping("/index")public String index() {return "index";}
}

运行结果如下:

打完收工。


http://www.mrgr.cn/news/68647.html

相关文章:

  • 大语言模型训练的全过程:预训练、微调、RLHF
  • 道品科技智慧农业中的自动气象检测站
  • mysql常见的一些配置项
  • MYSQL学习笔记(二)--认识索引、使用索引、索引失效
  • 高级图像处理工具
  • 软著补正有哪些类型
  • 指针的运用
  • 数据库基础(9) . DML-多表操作
  • C++11的简介
  • 【go从零单排】error错误处理及封装
  • 电脑Windows藏着的高手——WMIC命令
  • MySQL数据库基础(一) MySQL安装及数据类型
  • 电商企业店云账户提现探析
  • 【Ant.design pro】 formRef 用法
  • C++ 二分法
  • 中小跨境卖家如何选择物流?
  • 如何使用 Python 语言的正则表达式进行网页数据的爬取?
  • 算法 -排序 -插入,选择
  • 2024外贸独立站指南:3个提高转化的问题所在!
  • 反反爬-课上实验
  • LLM | 论文精读 | CVPR | 基于问题驱动图像描述的视觉问答增强引言
  • 【专题】2024年全球生物医药交易报告汇总PDF洞察(附原数据表)
  • 企业高效运转秘诀,揭秘工单系统双重价值
  • 【vue2.7.16系列】手把手教你搭建后台系统__刷新问题(17)
  • SpringMVC学习记录(五)之SpringMVC其他扩展
  • 关于我、重生到500年前凭借C语言改变世界科技vlog.16——万字详解指针概念及技巧