一.需求:
在开发一款可视化大屏工具的时候需要有文件的综合管理功能,该功能需要包含文件的上传,下载,预览,删除等功能
二.实现思路:
网上当然有很多的实现方式但是我们自己还是可以好好思考一下,毕竟改的好可以节省很多的时间,前端需要上传的接口很多但是明显只用写一个接口,上传的文件可能需要进行类型校验,大小校验,安全校验等。所以实现思路变成了写4个接口实现四个功能。上传的实现可以参考我主页的另一篇文章后端通用型文件上传功能springboot2.2.0设计实现,基本上都可以实现,实在不行就直接让前端转换为二进制文件传输到后端来实现。
下面是实现过程中使用的实现思路和实例代码,希望可以帮助到大家。
1.文件下载
1.1使用`HttpServletResponse`对象实现文件下载。这种方式可以通过设置响应头信息来实现文件下载,设置`Content-Disposition`头信息来指定文件名和文件类型。
在Controller类中添加一个处理文件下载请求的方法。例如:
@RestController
public class FileDownloadController {
@GetMapping("/download")
public ResponseEntity<byte[]> downloadFile() throws IOException {
// 读取文件内容
Path file = Paths.get("path/to/file");
byte[] data = Files.readAllBytes(file);
// 设置响应头信息
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
headers.setContentDispositionFormData("attachment", file.getFileName().toString());
// 返回文件内容和响应头信息
return new ResponseEntity<>(data, headers, HttpStatus.OK);
}
}
在这个例子中,我们使用`Files.readAllBytes()`方法读取文件内容,并将其存储在一个字节数组中。然后,我们创建一个`HttpHeaders`对象,设置响应头信息,包括内容类型和内容描述。最后,我们将文件内容和响应头信息一起返回给客户端。
在Postman中发送GET请求到`http://localhost:18080/download`,即可下载文件。
需要注意的是,这个例子中的文件路径是硬编码的,实际开发中应该根据需要动态获取文件路径,所以前端可能还需要将例如文件编号(ID),文件名什么的也作为参数发送过来。另外,如果需要支持大文件下载或断点续传等功能,可能需要进行更多的处理和优化。
我在这里最开始就是使用这个方式实现的,实现方式还是比较简单。
1.2使用`FileInputStream`和`ServletOutputStream`实现文件下载。这种方式可以通过读取文件内容并将其写入到响应输出流中来实现文件下载。
首先,需要创建一个`FileInputStream`对象来读取要下载的文件。然后,创建一个`ServletOutputStream`对象,将其与响应关联起来。最后,将文件内容从`FileInputStream`复制到`ServletOutputStream`中,并将其写入响应输出流中。
下面是实现过程中使用的实现思路和实例代码,希望可以帮助到大家。
@WebServlet("/download")
public class DownloadServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 获取要下载的文件路径
String filePath = "/path/to/file";
try(
// 创建 FileInputStream 对象
FileInputStream fis = new FileInputStream(new File(filePath));
){
// 设置响应头信息
response.setContentType("application/octet-stream");
response.setHeader("Content-Disposition", "attachment; filename=" + filePath);
// 创建 ServletOutputStream 对象
ServletOutputStream sos = response.getOutputStream();
// 读取文件内容并写入响应输出流中
byte[] buffer = new byte[4096];
int bytesRead;
while ((bytesRead = fis.read(buffer)) != -1) {
sos.write(buffer, 0, bytesRead);
}
}catch(Exception e){
System.out.println("文件下载错误!!!");
}finally{
try {
// 在finally块中刷新输出流
sos.flush();
} catch (IOException e) {
// 处理异常
e.printStackTrace();
}
// 关闭流
fis.close();
sos.close();
}}
在上面的示例中,我们首先获取要下载的文件路径,然后使用`FileInputStream`对象读取文件内容。接下来,我们设置响应头信息,包括内容类型和文件名。最后,我们使用`ServletOutputStream`对象将文件内容写入响应输出流中,并在完成后关闭流。
我后面改成这个方式实现里面还可以加入缓冲流来加速文件的下载但是要注意关闭打开的各种流,关闭各种流推荐用异常处理的finally{}来关闭,不然忘记关闭分分钟各种内存溢出问题。
1.3使用`Apache Commons FileUpload`库实现文件下载。这种方式可以通过解析表单数据并从服务器端读取文件内容来实现文件下载。
在Spring Boot中,可以使用`Apache Commons FileUpload`库实现文件下载。
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.4</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.6</version>
</dependency>
然后,在Controller中定义一个处理文件下载请求的方法:
@GetMapping("/download")
public void downloadFile(HttpServletResponse response) throws IOException {
try( //文件和输入输出流的准备
File file = new File(filePath);
InputStream inputStream = new FileInputStream(file);
OutputStream outputStream = response.getOutputStream();
){
//写到输出流中
byte[] buffer = new byte[1024];
int bytesRead;
while ((bytesRead = inputStream.read(buffer)) != -1) {
outputStream.write(buffer, 0, bytesRead);
}
}catch(Exception e){
System.out.println("文件下载错误!!!");
}finally{
try {
// 在finally块中刷新
outputStream.flush();
} catch (IOException e) {
// 处理异常
e.printStackTrace();
}
// 关闭流
inputStream.close();
outputStream.close();
}
}
在上面的示例中,我们首先设置了响应头信息,包括内容类型和文件名。然后,我们读取了要下载的文件的内容,并将其写入响应输出流中。最后,我们关闭了输入流和输出流。
需要注意的是,在使用`Apache Commons FileUpload`库实现文件上传时,需要将上传的文件保存到磁盘或数据库中。而在使用`Apache Commons FileUpload`库实现文件下载时,需要读取要下载的文件的内容,并将其写入响应输出流中。
2.文件上传:
2.1使用`MultipartFile`对象实现文件上传。这种方式可以通过解析请求中的文件数据并将其保存到服务器端来实现文件上传。
很好用我就是用的这个方法,简单易用。这个上传的实现可以参考我主页的另一篇文章《后端通用型文件上传功能springboot2.2.0设计实现》里面有具体的代码和解析什么的。
2.2使用`CommonsMultipartFile`类实现文件上传。这种方式是Apache Commons库中的一个类,可以通过该类来处理文件上传请求,并将文件数据保存到服务器端来实现文件上传。
在Java中,可以使用`CommonsMultipartFile`类实现文件上传。
首先,需要创建一个`CommonsMultipartFile`对象来表示要上传的文件。然后,将该对象添加到请求中,并设置相应的属性,例如文件名、文件类型等。最后,将请求提交到服务器端进行处理。
@WebServlet("/upload")
public class UploadServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 检查请求是否包含文件
if (!ServletFileUpload.isMultipartContent(request)) {
response.sendError(HttpServletResponse.SC_BAD_REQUEST, "Missing file");
return;
}
// 获取要上传的文件
CommonsMultipartFile file = (CommonsMultipartFile) request.getPart("file");
// 处理文件上传逻辑
// ...
// 返回响应
response.setContentType("text/plain");
response.setStatus(HttpServletResponse.SC_OK);
response.getWriter().println("File uploaded successfully");
}
}
在上面的示例中,我们首先检查请求是否包含文件。如果请求不包含文件,则返回一个错误响应。接下来,我们使用`getPart`方法获取要上传的文件,并将其转换为`CommonsMultipartFile`对象。然后,我们可以对该对象进行相应的处理,例如保存文件到磁盘或数据库中。最后,我们返回一个成功的响应。
2.3使用Thymeleaf模板引擎实现文件上传。这种方式可以通过在HTML表单中添加`enctype="multipart/form-data"`属性来指定表单类型为文件上传,并将表单数据提交到服务器端来实现文件上传。
首先,需要在pom.xml文件中添加Thymeleaf的依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
然后,在HTML页面中使用`th:form`和`th:action`属性定义表单和提交地址,使用`th:file_input`属性定义文件上传控件。例如:
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>File Upload</title>
</head>
<body>
<h1>File Upload</h1>
<form th:action="@{/upload}" method="post" enctype="multipart/form-data">
<div>
<label for="file">Select file to upload:</label>
<input type="file" id="file" name="file" th:file_input="|*files[]"/>
</div>
<button type="submit">Upload</button>
</form>
</body>
</html>
在上面的示例中,我们定义了一个包含文件上传控件的表单,并将表单提交到`/upload`地址。其中,`th:file_input`属性指定了文件上传控件的名称为`file`,并使用`|*files[]`表达式将上传的文件绑定到该控件上。
接下来,在Controller中处理文件上传请求:
@Controller
public class FileUploadController {
@PostMapping("/upload")
public String handleFileUpload(@RequestParam("file") MultipartFile file) {
if (!file.isEmpty()) {
try {
// 处理文件上传逻辑
// ...
} catch (Exception e) {
// 处理异常
e.printStackTrace();
}
} else {
// 处理文件为空的情况
// ...
}
return "redirect:/"; // 返回上一页
}
}
在上面的示例中,我们定义了一个处理文件上传请求的方法,该方法接收一个`MultipartFile`类型的参数,表示上传的文件。在方法中,我们可以对该文件进行处理,例如保存到磁盘或数据库中。最后,我们返回一个重定向响应,将用户带回上一页。
需要前端会Thymeleaf的相关调用,听说后端排错就是花时间去一个个的看有没有遗漏的地方,建议细心的后端用,尤其要注意的是FreeMarker的排错,很难很折磨。
3.文件预览
在Spring Boot中实现PDF预览的方法有很多种,其中一种常用的方法是通过使用pdf.js插件在前端展示PDF文件。在这种方法中,后台通过获取PDF文件的流,并将其传递给前端进行展示。具体实现步骤如下:
- 首先,在Spring Boo项目中创建一个Controller用于处理PDF文件的请求。可以使用@GetMapping注解指定请求路径.
- 在Controller中,使用FileInputstream读取PDF文件,并将其转换为字节数组或输入流
- 将读取到的PDF文件流返回给前端。可以使用ResponseEntity或streamingResponseBody来实现
@RestController
public class PdfController {
@GetMapping("/pdf")
public ResponseEntity<InputStreamResource> getPdf() throws IOException {
// 读取PDF文件 FileInputStream fis = new FileInputStream("path/to/your/pdf/file.pdf"); InputStreamResource resource = new InputStreamResource(fis);
// 设置响应头信息 String contentDisposition = "inline;filename=" + URLEncoder.encode("file.pdf", "UTF-8");
return ResponseEntity.ok() .contentType(MediaType.APPLICATION_PDF) .header("Content-Disposition", contentDisposition) .body(resource); } }
4.在前端页面中,引入pdf;is插件并设置相应的HTML元素来展示PDF文件。可以使用<iframe>标签或者obiect>标签来嵌入PDF文件。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>PDF预览</title>
</head>
<body>
<iframe src="/pdf" width="100%" height="800px"></iframe>
</body>
</html>
需要注意的是,需要将pdf.is的相关静态资源文件放置在合适的位置,并在前端页面中正确引用这些资源文件,以上是一种常见的实现PDF预览的方法,具体实现可以根据项目需求进行调整。这个方法的特点是大多数的文件都可以在后台先转化成为pdf文件然后再放到前端预览,可以满足大多数文件不同格式的预览需求。
三.思考:
3.1 关于功能性能优化的思考
主要的思考方向有加快数据的传输速度,比如文件流加缓冲流来加速读写。或者先将文件转化为结构化数据存储到数据库里面,然后用数据库去获取数据。
也可以往减少数据的传输的方向上去考虑。比如先将数据保存到一个全局静态变量里面然后需要的时候直接将该变量输出。但是这种方法也有一些缺点。例如,如果数据量很大,全局静态变量会占用大量的内存空间,可能会导致内存溢出等问题。此外,如果多个线程同时访问全局静态变量,还可能会出现线程安全问题。
3.2 关于功能安全性的思考
这个有很多可以思考的方向。
比如文件的内容检查,文件类型检查,文件加密,文件名加密,使用安全框架Spring Security等保证传输安全。
3.3 关于功能可用性的思考
这个有很多可以思考的方向。
例如文件预览时可以利用大部分文件可以转换成为pdf文件的特性去实现对于大部分文件的预览,提高了预览功能的可用性。
可以设计一个在文件处理过程中面对异常情况给返回数据初始值,以保证功能可用。
考虑网络卡顿等问题造成多次重复的上次,删除,下载等情况。
3.4 关于功能实现的思考
这个功能本质上就是保证文件数据在服务器和浏览器之间的传输。所以不规范实现就是只需要controller层,但是如果需要数据库记录上传文件的情况,那就最好写入逻辑层,如果更高一点需要转化为结构化数据存储在数据库里面,我们也要做好转化。