The article directories

  • File upload
    • An overview of the
      • commons-fileupload
      • The Servlet 3.0
    • Single file upload
    • Multi-file upload
    • Upload files with other parameters
      • Elegant back-end implementation
    • Multiple (file + parameter) uploads
      • Solution 1: Upload the Base64 file
      • Solution 2: Upload the FILE URL
  • File Uploading Principles
  • Problems encountered
    • Spring Boot upload file size limit
    • Excessively large uploaded files are intercepted
    • Customize the Tomcat working directory
    • Uploading files with Swagger doesn’t work

File upload

An overview of the

Spring supports pluggable MultipartResolver objects for file uploads. There are currently two implementations;

  • Prior to Servlet 2.5 and earlier versions, file uploads were implemented with the commons-Fileupload component.
  • Since the Servlet 3.0 specification, native support for file uploads has been provided, further simplifying application implementation.

commons-fileupload

To use Commons-Fileupload CommonsMultipartResolver to handle file uploads, we need to add the following dependencies:

<dependency>
    <groupId>commons-fileupload</groupId>
    <artifactId>commons-fileupload</artifactId>
</dependency>
Copy the code

The configuration defines CommonsMultipartResolver bean.

@Bean(name = "multipartResolver")
public CommonsMultipartResolver multipartResolver(a) {
    CommonsMultipartResolver multipartResolver = new CommonsMultipartResolver();
    multipartResolver.setMaxUploadSize(100000);
    return multipartResolver;
}
Copy the code

The Servlet 3.0

See MultipartAutoConfiguration. Java classes, the default configuration StandardServletMultipartResolver automatically, we don’t need to do anything

@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({ Servlet.class, StandardServletMultipartResolver.class, MultipartConfigElement.class })
@ConditionalOnProperty(prefix = "spring.servlet.multipart", name = "enabled", matchIfMissing = true)
@ConditionalOnWebApplication(type = Type.SERVLET)
@EnableConfigurationProperties(MultipartProperties.class)
public class MultipartAutoConfiguration {

	private final MultipartProperties multipartProperties;

	public MultipartAutoConfiguration(MultipartProperties multipartProperties) {
		this.multipartProperties = multipartProperties;
	}

	@Bean
	@ConditionalOnMissingBean({ MultipartConfigElement.class, CommonsMultipartResolver.class })
	public MultipartConfigElement multipartConfigElement(a) {
		return this.multipartProperties.createMultipartConfig();
	}

	@Bean(name = DispatcherServlet.MULTIPART_RESOLVER_BEAN_NAME)
	@ConditionalOnMissingBean(MultipartResolver.class)
	public StandardServletMultipartResolver multipartResolver(a) {
		StandardServletMultipartResolver multipartResolver = new StandardServletMultipartResolver();
		multipartResolver.setResolveLazily(this.multipartProperties.isResolveLazily());
		returnmultipartResolver; }}Copy the code

Common file uploading requirements are summarized as follows:

Single file upload

Front-end core code

<form method="POST" action="/upload-file" enctype="multipart/form-data">
    <table>
        <tr>
            <td><input type="file" name="file" /></td>
        </tr>
        <tr>
            <td><input type="submit" value="Submit" /></td>
        </tr>
    </table>
</form>
Copy the code

Back-end core code

@RequestMapping(value = "/upload-file", method = RequestMethod.POST)
public String submit(@RequestParam("file") MultipartFile file) {
    return "ok";
}
Copy the code

Multi-file upload

Front-end core code

<form method="POST" action="/upload-files" enctype="multipart/form-data">
    <table>
        <tr>
            <td>Select a file to upload</td>
            <td><input type="file" name="files" /></td>
        </tr>
        <tr>
            <td>Select a file to upload</td>
            <td><input type="file" name="files" /></td>
        </tr>
        <tr>
            <td>Select a file to upload</td>
            <td><input type="file" name="files" /></td>
        </tr>
        <tr>
            <td><input type="submit" value="Submit" /></td>
        </tr>
    </table>
</form>
Copy the code

Back-end core code

We need to note that each input field has the same name so that it can be accessed as a MultipartFile array:

    @RequestMapping(value = "/upload-files", method = RequestMethod.POST)
    public String submit(@RequestParam("files") MultipartFile[] files) {
        return "ok";
    }
Copy the code

Upload files with other parameters

Front-end core code

<form method="POST" action="/upload-files-with-data" enctype="multipart/form-data">
    <table>
        <tr>
            <td>Name</td>
            <td><input type="text" name="name" /></td>
        </tr>
        <tr>
            <td>Email</td>
            <td><input type="text" name="email" /></td>
        </tr>
        <tr>
            <td>Select a file to upload</td>
            <td><input type="file" name="file" /></td>
        </tr>
        <tr>
            <td><input type="submit" value="Submit" /></td>
        </tr>
    </table>
</form>
Copy the code

Back-end core code

In the controller, we can use the @requestParam annotation to get all the form data, or we can not use @RequestParam

@PostMapping("/upload-files-with-data")
public String submit(
            @RequestParam MultipartFile file, @RequestParam String name,
            String email) {
    return "ok";
}
Copy the code

Elegant back-end implementation

We can also wrap all the form fields in classes, which is handy when there are many other fields in the file.

public class FormDataWithFile {
    private String name;
    private String email;
    private MultipartFile file;
}
@PostMapping("/upload-files-with-data")
public String submit(FormDataWithFile formDataWithFile) {
    return "ok";
}
Copy the code

Multiple (file + parameter) uploads

The functional requirements are similar to uploading the following request:

[{"name": "a"."emainl": "b"."file":}, {"name": "a"."emainl": ""."file":}]Copy the code

But that doesn’t work. Here’s the solution:

Solution 1: Upload the Base64 file

Convert the file to a Base64 string, but the converted string is three times the size of the original image. (carefully)

[{"name": "a"."emainl": ""."fileBase64":"xxxxx"
    },
    {
        "name": "b"."emainl": ""."fileBase64":"xxxxx"}]Copy the code

Solution 2: Upload the FILE URL

Upload the image to the server, get the FILE URL, and then upload the file URL and other parameters to the back end

[{"name": "a"."emainl": ""."fileUrl":"xxxxx.png"
    },
    {
        "name": "b"."emainl": ""."fileUrl":"xxxxx.png"}]Copy the code

File Uploading Principles

The format of a file upload request is as follows:

POST /upload HTTP/1.1 
Host:xxx.org 
Content-type: multipart/form-data, boundary="boundaryStr"

--boundaryStr
content-disposition: form-data; name="name"

Name Of Picture
--boundaryStr
Content-disposition: attachment; name="picfile"; filename="picfile.gif"Content-type: image/gif Content-Transfer-Encoding: binary ... contentsof picfile.gif...
Copy the code

Where boundary specifies the boundary string of content segmentation;

Content-dispostion specifies that this is an attachment (file), including parameter names, file names;

Content-type specifies the file type.

Content-transfer-encoding Specifies the Content Transfer Encoding.

TomcatImplemented the Servlet3.0 specification, throughApplicationPartEncapsulate the file up-stream, where,DiskFileItemDiskFileItem represents an upload file entity that is generated when the request is resolved. Note that DiskFileItem declares oneThe temporary file, which is used to temporarily store the content of uploaded files. SpringMVC encapsulates the request entity of the upper layer again, and finally constructs asMultipartFilePass to the application.

The following is an example:

The generated temporary files are as follows:

This is the directory for temporary files, which can be configured



The temporary file is opened and its contents are as follows:

  • Parameters: the name

  • Parameters: the file



    After uploading, temporary files will be deleted

As you can see, parameters that are not file types are also written to temporary files.

Packet capture using Fiddler:

POST http://localhost:8080/upload-files-with-data HTTP / 1.1
cache-control: no-cache
Accept: * / *Host: localhost:8080
accept-encoding: gzip, deflate
content-type: multipart/form-data; boundary=--------------------------895818005136536360125479
content-length: 268707
Connection: keep-alive

-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 895818005136536360125479
Content-Disposition: form-data; name="name"

123
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 895818005136536360125479
Content-Disposition: form-data; name="file"; filename="test.txt"
Content-Type: text/plain

abc123
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 895818005136536360125479
Content-Disposition: form-data; name="file"; filename="1114289-20190110120111312-1475461850.png"
Content-Type: image/png

...contents of png...
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 895818005136536360125479
Copy the code

At this point, we have a rough idea of how HTTP upload files work. HTTP stores all data from the form to be uploaded in a certain format in the request body, as well as files.

  • Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryqj67FUBQUHXZj78GTo upload an attachment,
  • Among themboundaryRepresents a delimiter, used if there are multiple entries in the formboundaryEach form item is separated by------FormBoundaryBegan to------FormBoundaryAt the end. For example:
------FormBoundary
Content-Disposition: form-data; name="param1"

value1
------FormBoundary
Copy the code

The value of boundary is generated by the browser to ensure that it does not duplicate the uploaded content.

  • In each separator, we need to focusContent-DispositionMessage header, where the first parameter is always fixedform-data, name represents the form element attribute name, and the content after the carriage return newline character is the element value. There areContent-TypeRepresents the MIME type of the file that we are uploading, and we need to differentiate files on the server side based on this.
  • The last oneboundaryThere will be two more at the end--

HTTP encapsulates the data in a form into a request and sends it to the server. The server parses the received request according to this rule to complete the file upload function.

The following is an example of background parsing found on the Internet. DEBUG traces the code to analyze.

@WebServlet(urlPatterns = "/lakerfile")
public class FileUploadDemo extends HttpServlet {
    @Override
    public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        DiskFileItemFactory fac = new DiskFileItemFactory();
        ServletFileUpload upload = new ServletFileUpload(fac);
        upload.setFileSizeMax(10 * 1024 * 1024);
        upload.setSizeMax(20 * 1024 * 1024);
        if (ServletFileUpload.isMultipartContent(request)) { // Only Multipart requests are processed
                List<FileItem> list = upload.parseRequest(new ServletRequestContext(request));// Parse the packet
                for (FileItem item : list) {
                    if (item.isFormField()) {
                        String fileName = item.getFieldName();
                        String value = item.getString("UTF-8");
                    } else {
                        File file = newFile(realPath, name); item.write(file); . }}}Copy the code

Problems encountered

Spring Boot upload file size limit

spring:
  servlet:
    multipart:
      # Maximum file size (single)
      max-file-size: 10MB
      If the file is larger than this threshold, it will be written to disk. The unit of B/KB/MB is supported
      file-size-threshold: 0B
      # // Maximum request size (total)
      max-request-size: 100MB
Copy the code

These parameters are controlled by SpringMVC and are used to inject the file upload configuration of Servlet3.0 with the following associated classes:

public class MultipartConfigElement {
    private final String location;/ / = "";
    private final long maxFileSize;// = -1;
    private final long maxRequestSize;// = -1;
    private final int fileSizeThreshold;/ / = 0;
Copy the code

Excessively large uploaded files are intercepted

@ExceptionHandler(MaxUploadSizeExceededException.class)
public Response handleMaxSizeException(MaxUploadSizeExceededException e) {
    log.error(e.getMessage(), e);
    return Response.error(500."File too large!");
}
Copy the code

Customize the Tomcat working directory

Custom temporary file generation directory

server:
  tomcat:
    basedir: /laker/tmp
Copy the code

Uploading files with Swagger doesn’t work

  • allowMultiple=true: represents an array format parameter
  • dataType = "__file": represents the type of an array parameter
@apiOperation (value = "upload ", notes =" upload ")
@apiIMPLICITParams ({@apiIMPLICITParam (paramType = "form", name = "file", value = "file object ", Required = true, DataType = "__file"), @apiIMPLICITParam (paramType = "form", name = "files", value = "file array ", allowMultiple = true, dataType = "__file") })
public void test(@RequestParam("file") MultipartFile file, @RequestParam(value = "files", required = false) MultipartFile[] files) throws Exception {}Copy the code

Reference:

🍎QQ group [837324215] 🍎 pay attention to my public number [Java Factory interview officer], learn together 🍎🍎🍎