Spring Boot 上传文件

Spring Boot 上传文件教程展示了如何使用 Spring Boot 框架上传单个文件。

Spring 是流行的 Java 应用框架,而 Spring Boot 是 Spring 的演进,可以帮助轻松地创建独立的,生产级的基于 Spring 的应用。

HTML 表单的编码类型

POST 请求有三种编码 HTML 表单类型:

  • 应用/ x-www-form-urlencoded
  • 多部分/表单数据
  • 文字/纯文字

application/x-www-form-urlencoded是默认编码,其中值编码在由&分隔的键值元组中。 =字符用于键和值之间。 非字母数字字符采用百分比编码。 此编码类型不适用于二进制文件。

multipart/form-data用于非 acsii 数据和二进制文件。 input元素的type属性设置为file

text/plain用于调试。

Spring 上传文件示例

在下面的示例中,我们有一个 Web 表单来选择要上传到服务器的文件。 该文件被上传到/var/www/upload目录。

上传目录

/var/www目录是 Debian Linux 中 Web 内容的标准目录。

$ ls -ld /var/www/upload/
drwxrwxr-x 2 www-data www-data 4096 Dec  3 14:29 /var/www/upload/

我们将文件上传到/var/www/upload目录。 www-data组中的用户可以修改目录文件。 因此,运行 Web 服务器的用户必须在此组中。

应用

以下是 Spring Boot Web 应用的来源。

pom.xml
src
├───main
│   ├───java
│   │   └───com
│   │       └───zetcode
│   │           │   Application.java
│   │           ├───controller
│   │           │       MyController.java
│   │           ├───exception
│   │           │       StorageException.java
│   │           └───service
│   │                   StorageService.java
│   └───resources
│       │   application.properties
│       └───static
│               failure.html
│               index.html
│               success.html
└───test
    └───java

这是 Spring 应用的项目结构。

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
            xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
            xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">

    <modelVersion>4.0.0</modelVersion>
    <groupId>com.zetcode</groupId>
    <artifactId>springbootuploadfile</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>jar</packaging>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>11</maven.compiler.source>
        <maven.compiler.target>11</maven.compiler.target>
    </properties>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.5.9.RELEASE</version>
    </parent>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

这是 Maven pom.xml文件。

com/zetcode/controller/MyController.java

package com.zetcode.controller;

import com.zetcode.exception.StorageException;
import com.zetcode.service.StorageService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.multipart.MultipartFile;

@Controller
public class MyController {

    @Autowired
    private StorageService storageService;

    @RequestMapping(value = "/doUpload", method = RequestMethod.POST,
            consumes = {"multipart/form-data"})
    public String upload(@RequestParam MultipartFile file) {

        storageService.uploadFile(file);

        return "redirect:/success.html";
    }

    @ExceptionHandler(StorageException.class)
    public String handleStorageFileNotFound(StorageException e) {

        return "redirect:/failure.html";
    }
}

MyController从请求中读取文件并将其保存到所选目录中。

@Autowired
private StorageService storageService;

StoreageService将文件存储在磁盘上。

@RequestMapping(value = "/doUpload", method = RequestMethod.POST,
    consumes = {"multipart/form-data"})
public String upload(@RequestParam MultipartFile file) {

upload()方法映射到doUpload URL 模式。 由于我们正在将数据发送到服务器,因此我们使用 POST 请求。 请求参数具有MultipartFile类型。

return "redirect:/success.html";

成功上传文件后,我们会显示一条消息。

@ExceptionHandler(StorageException.class)
public String handleStorageFileNotFound(StorageException e) {

    return "redirect:/failure.html";
}

我们有StorageException的处理程序。

com/zetcode/StorageException.java

package com.zetcode.exception;

public class StorageException extends RuntimeException {

    public StorageException(String message) {
        super(message);
    }

    public StorageException(String message, Throwable cause) {
        super(message, cause);
    }
}

这是我们的自定义StorageException。 当文件无法存储在文件系统上时,将引发该错误。

com/zetcode/service/StorageService.java

package com.zetcode.service;

import com.zetcode.exception.StorageException;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;

@Service
public class StorageService {

    @Value("${upload.path}")
    private String path;

    public void uploadFile(MultipartFile file) {

        if (file.isEmpty()) {
            throw new StorageException("Failed to store empty file");
        }

        try {
            var fileName = file.getOriginalFilename();
            var is = file.getInputStream();

            Files.copy(is, Paths.get(path + fileName),
                    StandardCopyOption.REPLACE_EXISTING);
        } catch (IOException e) {

            var msg = String.format("Failed to store file", file.getName());

            throw new StorageException(msg, e);
        }

    }
}

StorageService从输入流复制数据并将其保存在磁盘上。

@Value("${upload.path}")
private String path;

我们使用@Value注解从application.properties文件中读取上传目录。

if (file.isEmpty()) {
    throw new StorageException("Failed to store empty file");
}

我们确保已使用isEmpty()方法选择了一个文件。

var fileName = file.getOriginalFilename();

我们使用getOriginalFilename()方法获得文件名。

var is = file.getInputStream();

我们使用getInputStream()方法获得输入流。

Files.copy(is, Paths.get(path + fileName),
        StandardCopyOption.REPLACE_EXISTING);

该文件被复制到从与Files.copy()输入流源的目标目录。

resources/application.properties

upload.path=/var/www/upload/

application.properties中,我们有一个upload.path属性,用于指定上传目录。

resources/static/index.html

<!DOCTYPE html>
<html>
    <head>
        <title>Uploading file</title>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
    </head>
    <body>
        <h1>Uploading file</h1>

        <form action="/doUpload" method="post" enctype="multipart/form-data">
            <label>Enter file</label>
            <input type="file" name="file">
            <button type="submit">Upload</button>
        </form>
    </body>
</html>

这是主页。 它是src/main/resources/static目录中的静态文件。 它包含一个用于选择文件并将其发送到 Spring 应用的表单。

<form action="/doUpload" method="post" enctype="multipart/form-data">

我们选择了doUpload URL 模式。 此表单创建的请求将由 Spring 控制器处理。 enctype属性指定multipart/form-data编码类型,这是使用 HTML 格式上传文件所必需的。

<input type="file" name="file">

input标签的type属性使用户可以选择文件。

<button type="submit">Upload</button>

最后,这是一个提交按钮。

resources/static/success.html

<!DOCTYPE html>
<html>
    <head>
        <title>Success</title>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
    </head>
    <body>
        <p>File successfully uploaded</p>
    </body>
</html>

文件成功上传到服务器后,将显示success.html

resources/static/failure.html

<!DOCTYPE html>
<html>
    <head>
        <title>Failure</title>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
    </head>
    <body>
        <p>Failed to upload file</p>
    </body>
</html>

文件上传失败时,将显示failure.html

Application.java

package com.zetcode;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class Application  {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

这段代码设置了 Spring Boot 应用。

在本教程中,我们学习了如何在 Spring 应用中上传文件。

Python教程

Java教程

Web教程

数据库教程

图形图像教程

大数据教程

开发工具教程

计算机教程