# Spring Boot 上传文件

> 原文： [http://zetcode.com/springboot/uploadfile/](http://zetcode.com/springboot/uploadfile/)

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

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

## HTML 表单的编码类型

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

*   `application/x-www-form-urlencoded`
*   `multipart/form-data`
*   `text/plain`

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

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

`text/plain`用于调试。

## Spring 上传文件示例

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

### 上传目录

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

```java
$ 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 应用的来源。

```java
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`

```java
<?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`

```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`从请求中读取文件并将其保存到所选目录中。

```java
@Autowired
private StorageService storageService;

```

`StoreageService`将文件存储在磁盘上。

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

```

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

```java
return "redirect:/success.html";

```

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

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

    return "redirect:/failure.html";
}

```

我们有`StorageException`的处理器。

`com/zetcode/StorageException.java`

```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`

```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`从输入流复制数据并将其保存在磁盘上。

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

```

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

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

```

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

```java
var fileName = file.getOriginalFilename();

```

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

```java
var is = file.getInputStream();

```

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

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

```

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

`resources/application.properties`

```java
upload.path=/var/www/upload/

```

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

`resources/static/index.html`

```java
<!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 应用的表单。

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

```

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

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

```

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

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

```

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

`resources/static/success.html`

```java
<!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`

```java
<!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`

```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 应用中上传文件。 您可能也对相关教程感兴趣： [Spring Boot `@ExceptionHandler`教程](/springboot/exceptionhandler/)， [Spring Boot `@PathVariable`教程](/springboot/pathvariable/)， [Spring Boot `@RequestParam`教程](/springboot/requestparam/)， [Spring Boot REST H2 教程](/articles/springbootresth2/)，[独立的 Spring 应用](/articles/standalonespring/)， [Java 教程](/lang/java/)。