在Spring Boot程式中上傳和下載檔案

譚朝紅發表於2019-04-10

Spring Boot檔案上傳和下載

檔案上傳和下載是Web應用程式比較常用的功能之一,在本章節中,我將以一個簡單的案例來講解在Spring Boot中如何進行檔案的上傳與下載。在開始正文之前,我們通過一張思維導圖來了解一下檔案上傳與下載的簡單流程:

檔案上傳與下載思維導圖
檔案上傳與下載思維導圖

1. 檔案上傳

對於檔案上傳,控制器中對應的上傳方法的引數必須是MultipartFile物件,MultipartFile物件可以是一個陣列物件,也可以是單個物件,如果是一個陣列物件,則可以進行多檔案上傳;這裡我們僅演示單個檔案上傳,下面的程式碼展示了檔案上傳方法的基本結構:

@PostMapping(value = "/upload",consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
@ResponseBody
public String fileUpload(@RequestParam("file") MultipartFile file) throws IOException {
	return null;
}
複製程式碼

接下來,我們使用FileOutputStream物件將客戶端上傳的檔案寫入到磁碟中,並返回**“File is upload successfully”**的提示資訊,下面是檔案上傳完整的程式碼:

package com.ramostear.application.controller;

import com.ramostear.application.model.FileInfo;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.PostConstruct;
import java.io.*;
import java.util.HashMap;
import java.util.Map;

/**
 * @author : ramostear
 * @date : 2019/3/8 0008-15:35
 */
@Controller
public class FileController {


    @Value ( "${file.upload.root.dir}" )
    String fileUploadRootDir;

    private static Map<String,FileInfo> fileRepository = new HashMap<>();

    @PostConstruct
    public void initFileRepository(){
        FileInfo file1 = new FileInfo ().setFileName ( "bg1.jpg" );
        FileInfo file2 = new FileInfo ().setFileName ( "bg2.jpg" );
        FileInfo file3 = new FileInfo ().setFileName ( "bg3.jpg" );
        fileRepository.put ( file1.getName (),file1 );
        fileRepository.put ( file2.getName (),file2 );
        fileRepository.put ( file3.getName (),file3 );
    }

@PostMapping(value = "/upload",consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
@ResponseBody
public String fileUpload(@RequestParam("file") MultipartFile file) throws IOException {

        File convertFile = new File ( fileUploadRootDir+file.getOriginalFilename ());
        FileOutputStream fileOutputStream = new FileOutputStream ( convertFile );
        fileOutputStream.write ( file.getBytes () );
        fileOutputStream.close ();

        FileInfo fileInfo = new FileInfo()
                .setFileName ( file.getOriginalFilename());

        fileRepository.put ( fileInfo.getName (),fileInfo);

        return "File is upload successfully";
    }
}

複製程式碼

fileRepository用於存放已上傳檔案的索引資訊。

2. 檔案下載

在Spring Boot應用程式中,我們可以使用InputStreamResource物件來下載檔案,在下載檔案的方法中,我們需要通過Response來設定HttpHeander物件的相關屬性,如Content-DispositionCache-ControlPragmaExpires等屬性。除此之外,還需要指定Response的響應型別。下面的程式碼給出了檔案下載的詳細資訊:

@GetMapping("/download/{fileName}")
@ResponseBody
public ResponseEntity<Object> downloadFile(@PathVariable(name = "fileName") String fileName) throws FileNotFoundException {

        File file = new File ( fileUploadRootDir+fileName);
        InputStreamResource resource = new InputStreamResource ( new FileInputStream ( file ) );

        HttpHeaders headers = new HttpHeaders();
        headers.add ( "Content-Disposition",String.format("attachment;filename=\"%s",fileName));
        headers.add ( "Cache-Control","no-cache,no-store,must-revalidate" );
        headers.add ( "Pragma","no-cache" );
        headers.add ( "Expires","0" );

        ResponseEntity<Object> responseEntity = ResponseEntity.ok()
                .headers ( headers )
                .contentLength ( file.length ())
                .contentType(MediaType.parseMediaType ( "application/txt" ))
                .body(resource);

        return responseEntity;
    }
複製程式碼

3. 程式碼清單

3.1 檔案上傳和下載控制器

下面給出的是完整的檔案上傳和下載的程式碼:

package com.ramostear.application.controller;

import com.ramostear.application.model.FileInfo;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.InputStreamResource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.PostConstruct;
import java.io.*;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;

/**
 * @author : ramostear
 * @date : 2019/3/8 0008-15:35
 */
@Controller
public class FileController {


    @Value ( "${file.upload.root.dir}" )
    String fileUploadRootDir;

    private static Map<String,FileInfo> fileRepository = new HashMap<>();

    @PostConstruct
    public void initFileRepository(){
        FileInfo file1 = new FileInfo ().setFileName ( "bg1.jpg" );
        FileInfo file2 = new FileInfo ().setFileName ( "bg2.jpg" );
        FileInfo file3 = new FileInfo ().setFileName ( "bg3.jpg" );
        fileRepository.put ( file1.getName (),file1 );
        fileRepository.put ( file2.getName (),file2 );
        fileRepository.put ( file3.getName (),file3 );
    }

    @GetMapping("/files")
    public String files(Model model){
        Collection<FileInfo> files = fileRepository.values ();
        model.addAttribute ( "data",files );
        return "files";
    }


    @PostMapping(value = "/upload",consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
    @ResponseBody
    public String fileUpload(@RequestParam("file") MultipartFile file) throws IOException {

        File convertFile = new File ( fileUploadRootDir+file.getOriginalFilename ());
        FileOutputStream fileOutputStream = new FileOutputStream ( convertFile );
        fileOutputStream.write ( file.getBytes () );
        fileOutputStream.close ();

        FileInfo fileInfo = new FileInfo()
                .setFileName ( file.getOriginalFilename());

        fileRepository.put ( fileInfo.getName (),fileInfo);

        return "File is upload successfully";
    }

    @GetMapping("/download/{fileName}")
    @ResponseBody
    public ResponseEntity<Object> downloadFile(@PathVariable(name = "fileName") String fileName) throws FileNotFoundException {

        File file = new File ( fileUploadRootDir+fileName);
        InputStreamResource resource = new InputStreamResource ( new FileInputStream ( file ) );

        HttpHeaders headers = new HttpHeaders();
        headers.add ( "Content-Disposition",String.format("attachment;filename=\"%s",fileName));
        headers.add ( "Cache-Control","no-cache,no-store,must-revalidate" );
        headers.add ( "Pragma","no-cache" );
        headers.add ( "Expires","0" );

        ResponseEntity<Object> responseEntity = ResponseEntity.ok()
                .headers ( headers )
                .contentLength ( file.length ())
                .contentType(MediaType.parseMediaType ( "application/txt" ))
                .body(resource);

        return responseEntity;
    }



}

複製程式碼

3.2 資料模型

建立一個檔案資訊資料模型作為上傳檔案資訊的載體,下面是FileInfo.java的程式碼:

package com.ramostear.application.model;

import lombok.Data;

import java.util.Date;

/**
 * @author : ramostear
 * @date  : 2019/3/8 0008-15:25
 */
@Data
public class FileInfo {

    private String name;
    private Date uploadTime = new Date();

    public FileInfo setFileName(String name){
        this.setName ( name );
        return this;
    }

}

複製程式碼

3.3 Maven build 檔案

下面是本次demo應用程式的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>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.1.3.RELEASE</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>com.ramostear</groupId>
	<artifactId>file-handling</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>file-handling</name>
	<description>Demo project for Spring Boot</description>

	<properties>
		<java.version>1.8</java.version>
	</properties>

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

		<dependency>
			<groupId>org.projectlombok</groupId>
			<artifactId>lombok</artifactId>
			<optional>true</optional>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
	</dependencies>

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

</project>

複製程式碼

注:本次案例使用freemarker模板引擎作為檢視模板

3.4 配置檔案

application.properties檔案主要設定了freemarker的相關屬性以及自定義的**file.upload.root.dir **屬性:

spring.freemarker.cache=false
spring.freemarker.prefix=
spring.freemarker.suffix=.html
spring.freemarker.enabled=true
spring.freemarker.charset=UTF-8
spring.freemarker.template-loader-path=classpath:/templates/
file.upload.root.dir = C:/work/upload/
複製程式碼

file.upload.root.dir自定義屬性設定了檔案上傳的更目錄為:C:/work/upload/

3.5 檢視

在檢視檔案中,建立了一個form表單用於上傳檔案,另外還建立了一個已上傳檔案列表,提供檔案下載操作。

檔案上傳表單:

上傳檔案form表單
上傳檔案form表單

檔案下載列表:

檔案下載列表
檔案下載列表

說明:檔案上使用的是非同步上傳方式進行上傳,沒有使用同步提交form表單的方式進行

檔案上傳非同步操作程式碼如下:

$("#upload").on("click",function () {
           var fileObj = document.getElementById("file").files[0];
           var form = new FormData();
           form.append("file",fileObj);
           var xhr = new XMLHttpRequest();
           xhr.open("post","http://localhost:8080/upload",true);
           xhr.onload = function(event){
               alert(event.currentTarget.responseText);
               window.location.href = window.location.href;
           };
           xhr.send(form);
        });
複製程式碼

4. 打包執行

使用Maven命令對應用程式進行打包,下面是maven打包的命令:

mvn clean install
複製程式碼

在控制檯視窗中執行上述命令,等待maven打包。若控制檯中顯示**“BUILD SUCCESS”**資訊,你可以在當前工程目錄下的target資料夾中找到相應的JAR檔案。

現在,你可以使用下面的命令來執行JAR檔案:

java -jar YOUR_JARFILE_NAME
複製程式碼

JAR檔案成功啟動後,你可以在控制檯視窗中看到如下的資訊:

控制檯視窗資訊
控制檯視窗資訊

5. 測試

開啟瀏覽器並在位址列輸入:http://localhost:8080/files 。下面是成功請求後的瀏覽器截圖:

檔案列表
檔案列表

接下來,點選其中任意一個download按鈕,測試檔案下載功能是否正常:

下載檔案
下載檔案

最後,我們測試一下檔案上傳功能是否正常。在進行測試之前,我們先看一下檔案上傳目錄中儲存的檔案資訊:

檔案上傳目錄
檔案上傳目錄

接下來,我們選擇一份需要上傳的檔案,然後點選upload按鈕上傳檔案:

上傳檔案
上傳檔案

此時,檔案以及上傳成功,我們再次觀察檔案上傳目錄中的檔案資訊,以驗證檔案是否成功寫入磁碟:

檔案上傳目錄對比
檔案上傳目錄對比

6. 附件

本章節的全部原始碼已經上傳至Github程式碼倉庫中,你可以訪問下面的地址獲得全部的原始碼:github.com/ramostear/S…

作者:譚朝紅,原文:在Spring Boot程式中上傳和下載檔案

相關文章