使用 Spring Boot 应用程序上传和下载文件

Upload and download the files using Spring Boot Application - PixelTrice使用 Spring Boot 应用程序上传和下载文件https://www.pixeltrice.com/upload-and-download-the-files-using-spring-boot-application/

如果您在互联网上,那么您肯定会上传或下载一些文件,如电影、歌曲或任何文件,如 pdf、图像等。这些是任何应用程序或软件的非常常用的功能。

所以,不用浪费太多时间,让我们进入主题,即如何在 Spring Boot 应用程序中实现上传和下载功能。

在这里,我们将构建一个用于上传和下载文件的 Restful API,我们还将使用 Javascript 开发一个非常简单的前端来上传和下载文件。

别担心,我不会使用大量的 javascript。只需五行代码即可实现我们的 Restful API。它是完全可选的。如果您不感兴趣或者如果您想在您的应用程序中添加前端代码,您可以跳过,然后请从本文最后一节下载源代码。

以下是最终的应用程序。

使用 Spring Boot 应用程序上传和下载文件

那么,让我们开始吧

注意:视频教程可在本文底部找到。

第 1 步:创建一个 Spring Boot 应用程序。

您可以从任何提到的选项创建。

  1. 使用 Spring Initialzr 创建一个项目。
  2. 使用 Spring Boot CLI 创建项目。

从 Spring Initialzr 创建一个项目。

  1. 转到https://start.spring.io/
  2. 输入组名称。com.pixeltrice
  3. 给出工件 ID。上传-下载-文件-with-spring-boot
  4. 添加Spring Web依赖项。
  5. 单击生成项目并开始下载。
  6. 下载完成后,解压缩文件并将其导入 IDE,例如 Eclipse。
  7. 按照以下步骤在 Eclipse 中导入文件。

选择 File -> Import -> Existing Maven Projects -> Browse -> 选择文件夹 upload-download-files-with-spring-boot-> Finish。

从 Spring Boot CLI 创建项目。

  1. 在您的系统中下载并安装 Spring Boot CLI。请按照文章从 Spring Boot CLI 运行应用程序
  2. 打开命令提示符并键入以下命令。
spring init --name=upload-download-files-with-spring-boot --dependencies=web upload-download-files-with-spring-boot

使用 Spring Boot 应用程序上传和下载文件

3. 按 Enter 并检查项目的以下路径

使用 Spring Boot 应用程序上传和下载文件

Project extracted to 'C:\Users\1302143\upload-download-files-with-spring-boot'

伟大的!您成功完成了第一步。

步骤 2:在 application.properties 文件中配置服务器和文件存储属性

在这里,我们将配置以下内容。

  1. 在 Spring Boot Application 中启用分段文件上传。
  2. 定义文件的最大上传大小。
  3. 配置上传文件的保存目录。

转到 application.properties 文件。

# Enable multipart uploads
spring.servlet.multipart.enabled=true
# Threshold after which files are written to disk.
spring.servlet.multipart.file-size-threshold=2KB
# Max file size.
spring.servlet.multipart.max-file-size=300MB
# Max Request Size
spring.servlet.multipart.max-request-size=315MB

## File Storage Properties
# All files uploaded through the REST API will be stored in this directory
file.upload-dir=C:/Users/1302143/uploads

注意:您可以更改路径file.upload-dir 的值。如果您是开发人员并希望在您的项目中实现上传功能,请根据您的要求更改上传存储文件位置。

第 3 步:创建 Pojo 或模型类并绑定或分配 application.properties 文件的属性

Spring Boot 为我们提供了一个非常独特的功能,可以自动将属性值绑定或分配给 Pojo 类中存在的参数。

为此,我们需要在 Pojo 类中使用@ConfigurationProperties注解,如下所示。

package com.pixeltrice.uploaddownloadfileswithspringboot;

import org.springframework.boot.context.properties.ConfigurationProperties;

@ConfigurationProperties(prefix = "file")
	public class FileStoragePojo {

		private String uploadDir;

	    public String getUploadDir() {
	        return uploadDir;
	    }

	    public void setUploadDir(String uploadDir) {
	        this.uploadDir = uploadDir;
	    }
	}

因此,在上面的代码中,prefix = “file”用于查找以“file”开头的属性,该属性存在于application.properties 文件中,并将该值分配给FileStoragePojo的 uploadDir变量java类。

在我们的例子中,一旦你运行 Spring Boot 应用程序,uploadDir 将被赋值为 C:/Users/1302143/uploads,因为在application.properties文件中,我们只有一个以前缀“file”开头的属性,即

file.upload-dir=C:/Users/1302143/uploads

注意:如果您以后想在 application.properties 中添加更多以“file”开头的属性,那么您只需在 Pojo 类中添加参数,这样它就会自动为该参数分配值。

第 4 步:在主类中启用配置属性

为了启用该属性,我们需要在主类或带有@SpringBootApplication标记的类中添加@EnableConfigurationProperties注解,或者您可以将@EnableConfigurationProperties添加到任何其他配置类中。

package com.pixeltrice.uploaddownloadfileswithspringboot;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.EnableConfigurationProperties;

@SpringBootApplication
@EnableConfigurationProperties({
    FileStoragePojo.class
})
public class UploadDownloadFilesWithSpringBootApplication {

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

}

恭喜!您已经完成了所有配置部分,现在我们可以继续编写实际的代码行来上传和下载文件。

第 5 步:创建一个用于存储和检索文件的服务类。

我们将创建一个名为FileStorageService.java的类,以便将文件存储在我们的系统上并检索它。

package com.pixeltrice.uploaddownloadfileswithspringboot;

import java.io.IOException;
import java.net.MalformedURLException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.Resource;
import org.springframework.core.io.UrlResource;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;


@Service
public class FileStorageService {

    private final Path fileStorageLocation;
	
	
	
	@Autowired
    public FileStorageService(FileStoragePojo fileStoragePojo) {
        this.fileStorageLocation = Paths.get(fileStoragePojo.getUploadDir())
                .toAbsolutePath().normalize();

        try {
            Files.createDirectories(this.fileStorageLocation);
        } catch (Exception ex) {
            throw new FileStorageException("Unable to create the directory where the uploaded files will be stored.", ex);
        }
    }
	
	
	 public String storeFile(MultipartFile file) {
	        // Normalize file name
	        String fileName = StringUtils.cleanPath(file.getOriginalFilename());

	        try {
	            // Check if the file's name contains invalid characters
	            if(fileName.contains("..")) {
	                throw new FileStorageException("Sorry! Filename contains invalid path sequence " + fileName);
	            }

	            // Copy file to the target location (Replacing existing file with the same name)
	            Path targetLocation = this.fileStorageLocation.resolve(fileName);
	            Files.copy(file.getInputStream(), targetLocation, StandardCopyOption.REPLACE_EXISTING);

	            return fileName;
	        } catch (IOException ex) {
	            throw new FileStorageException("Could not store file " + fileName + ". Please try again!", ex);
	        }
	    }

	
	   public Resource loadFileAsResource(String fileName) {
	        try {
	            Path filePath = this.fileStorageLocation.resolve(fileName).normalize();
	            Resource resource = new UrlResource(filePath.toUri());
	            if(resource.exists()) {
	                return resource;
	            } else {
	                throw new MentionedFileNotFoundException("File not found " + fileName);
	            }
	        } catch (MalformedURLException ex) {
	            throw new MentionedFileNotFoundException("File not found " + fileName, ex);
	        }
	    }
	
	
}

服务类中存在的每一行代码的解释。

  1. FileStorageService 构造函数的解释。
 public FileStorageService(FileStoragePojo fileStoragePojo) {
        this.fileStorageLocation = Paths.get(fileStoragePojo.getUploadDir())
                .toAbsolutePath().normalize();

        try {
            Files.createDirectories(this.fileStorageLocation);
        } catch (Exception ex) {
            throw new FileStorageException("Unable to create the directory where the uploaded files will be stored.", ex);
        }
    }
  • 在上面的代码行中,我们定义了一个参数化的构造函数,我们在其中传递了一个我们已经在上一段中创建的FileStoragePojo对象。
  • 指定上传后文件将存储的路径。
this.fileStorageLocation = Paths.get(fileStoragePojo.getUploadDir())
                .toAbsolutePath().normalize();
  • 在将保存上传文件的路径中创建文件夹或目录。
Files.createDirectories(this.fileStorageLocation);

2.storeFile()方法说明

 public String storeFile(MultipartFile file) {
	        // Normalize file name
	        String fileName = StringUtils.cleanPath(file.getOriginalFilename());

	        try {
	            // Check if the file's name contains invalid characters
	            if(fileName.contains("..")) {
	                throw new FileStorageException("Sorry! Filename contains invalid path sequence " + fileName);
	            }

	            // Copy file to the target location (Replacing existing file with the same name)
	            Path targetLocation = this.fileStorageLocation.resolve(fileName);
	            Files.copy(file.getInputStream(), targetLocation, StandardCopyOption.REPLACE_EXISTING);

	            return fileName;
	        } catch (IOException ex) {
	            throw new FileStorageException("Could not store file " + fileName + ". Please try again!", ex);
	        }
	    }
  • 在第一行代码中,我们从文件保存的路径中获取文件的实际名称,例如,假设我们在以下路径C:/Users/1302143/uploads/中上传了一个名为 springBoot.pdf 的文件springBoot.pdf

现在在路径上方,我们使用以下代码行提取文件的原始名称 springBoot.pdf。

 String fileName = StringUtils.cleanPath(file.getOriginalFilename());

所以fileName = springBoot.pdf

同样,你可以很容易地得到另一行代码的解释,我以注释的形式提到过。

注意: storeFile() 方法的全部目的是返回原始文件名。

3. loadFileAsResource(String fileName) 方法说明

该方法将以资源对象的形式返回存储或上传的文件。

 public Resource loadFileAsResource(String fileName) {
	        try {
	            Path filePath = this.fileStorageLocation.resolve(fileName).normalize();
	            Resource resource = new UrlResource(filePath.toUri());
	            if(resource.exists()) {
	                return resource;
	            } else {
	                throw new MentionedFileNotFoundException("File not found " + fileName);
	            }
	        } catch (MalformedURLException ex) {
	            throw new MentionedFileNotFoundException("File not found " + fileName, ex);
	        }
	    }

精彩的!我对 Service 类中编写的代码有一些想法。如果您在上面的代码中注意到我们正在使用某种异常处理类,例如FileStorageExceptionMentionedFileNotFoundException

实际上这些异常不是预定义的类,我们必须自己创建它。

注意:编写在FileStorageService.java服务类中的代码我们将在后面的部分中创建 API 时使用。

第 6 步:创建异常处理类

  1. FileStorageException.java
package com.pixeltrice.uploaddownloadfileswithspringboot;

	public class FileStorageException extends RuntimeException {
	    public FileStorageException(String message) {
	        super(message);
	    }

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

2. MentionedFileNotFoundException.java

package com.pixeltrice.uploaddownloadfileswithspringboot;

import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.http.HttpStatus;

@ResponseStatus(HttpStatus.NOT_FOUND)
	public class MentionedFileNotFoundException extends RuntimeException {
	    public MentionedFileNotFoundException(String message) {
	        super(message);
	    }

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

在上面的代码中,我使用了 @ResponseStatus(HttpStatus.NOT_FOUND),如果没有找到提到的文件,它将返回 404 状态。

第 7 步:为 API 创建一个控制器类以上传和下载文件。

package com.pixeltrice.uploaddownloadfileswithspringboot;

import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

import javax.servlet.http.HttpServletRequest;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.Resource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.support.ServletUriComponentsBuilder;

@RestController
public class FileStorageController {

	@Autowired
    private FileStorageService fileStorageService;
	
	 @PostMapping("/upload-single-file")
	    public UploadFileResponse uploadSingleFile(@RequestParam("file") MultipartFile file) {
	        String fileName = fileStorageService.storeFile(file);

	        String fileDownloadUri = ServletUriComponentsBuilder.fromCurrentContextPath()
	                .path("/download-file/")
	                .path(fileName)
	                .toUriString();

	        return new UploadFileResponse(fileName, fileDownloadUri,
	                file.getContentType(), file.getSize());
	    }
	 
	 @PostMapping("/upload-multiple-files")
	    public List<UploadFileResponse> uploadMultipleFiles(@RequestParam("files") MultipartFile[] files) {
	        return Arrays.asList(files)
	                .stream()
	                .map(file -> uploadSingleFile(file))
	                .collect(Collectors.toList());
	    }

	    @GetMapping("/download-file/{fileName:.+}")
	    public ResponseEntity<Resource> downloadFile(@PathVariable String fileName, HttpServletRequest request) {
	        // Load file as Resource
	        Resource resource = fileStorageService.loadFileAsResource(fileName);

	        // Try to determine file's content type
	        String contentType = null;
	        try {
	            contentType = request.getServletContext().getMimeType(resource.getFile().getAbsolutePath());
	        } catch (IOException ex) {
	            System.out.print("Could not determine file type.");
	        }

	        // Fallback to the default content type if type could not be determined
	        if(contentType == null) {
	            contentType = "application/octet-stream";
	        }

	        return ResponseEntity.ok()
	                .contentType(MediaType.parseMediaType(contentType))
	                .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + resource.getFilename() + "\"")
	                .body(resource);
	    }
}

控制器类中存在的每个 api 的说明。

  1. 上传文件
 @PostMapping("/upload-single-file")
	    public UploadFileResponse uploadSingleFile(@RequestParam("file") MultipartFile file) {
	        String fileName = fileStorageService.storeFile(file);

	        String fileDownloadUri = ServletUriComponentsBuilder.fromCurrentContextPath()
	                .path("/download-file/")
	                .path(fileName)
	                .toUriString();

	        return new UploadFileResponse(fileName, fileDownloadUri,
	                file.getContentType(), file.getSize());
	    }

上面的 api 用于上传单个文件并存储在系统上的上述位置。

2.上传多个文件

@PostMapping("/upload-multiple-files")
	    public List<UploadFileResponse> uploadMultipleFiles(@RequestParam("files") MultipartFile[] files) {
	        return Arrays.asList(files)
	                .stream()
	                .map(file -> uploadSingleFile(file))
	                .collect(Collectors.toList());
	    }

通过使用上述 api,我们可以一起上传多个文件。

3. 下载文件的API。

@GetMapping("/download-file/{fileName:.+}")
	    public ResponseEntity<Resource> downloadFile(@PathVariable String fileName, HttpServletRequest request) {
	        // Load file as Resource
	        Resource resource = fileStorageService.loadFileAsResource(fileName);

	        // Try to determine file's content type
	        String contentType = null;
	        try {
	            contentType = request.getServletContext().getMimeType(resource.getFile().getAbsolutePath());
	        } catch (IOException ex) {
	            System.out.print("Could not determine file type.");
	        }

	        // Fallback to the default content type if type could not be determined
	        if(contentType == null) {
	            contentType = "application/octet-stream";
	        }

	        return ResponseEntity.ok()
	                .contentType(MediaType.parseMediaType(contentType))
	                .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + resource.getFilename() + "\"")
	                .body(resource);
	    }

如果您在上面的代码中注意到我们使用了一个名为UploadFileResponse的对象作为返回类型。基本上我们需要创建这个类,它只负责在该 API 被触发或调用时正确返回响应。

UploadFileResponse.java

package com.pixeltrice.uploaddownloadfileswithspringboot;

public class UploadFileResponse {

	    private String fileName;
	    private String fileDownloadUri;
	    private String fileType;
	    private long size;

	    public UploadFileResponse(String fileName, String fileDownloadUri, String fileType, long size) {
	        this.fileName = fileName;
	        this.fileDownloadUri = fileDownloadUri;
	        this.fileType = fileType;
	        this.size = size;
	    }

		public String getFileName() {
			return fileName;
		}

		public void setFileName(String fileName) {
			this.fileName = fileName;
		}

		public String getFileDownloadUri() {
			return fileDownloadUri;
		}

		public void setFileDownloadUri(String fileDownloadUri) {
			this.fileDownloadUri = fileDownloadUri;
		}

		public String getFileType() {
			return fileType;
		}

		public void setFileType(String fileType) {
			this.fileType = fileType;
		}

		public long getSize() {
			return size;
		}

		public void setSize(long size) {
			this.size = size;
		}
	    
	    
	    
}

第 8 步:运行应用程序并从 POSTMAN 进行测试

  1. 上传单个文件

在这一步中,我们从POSTMAN中调用API http://localhost:8080/upload-single-file,并以Key-Value对的形式传递参数,如图所示。一旦 API 被调用,您将得到如下所示的响应。

使用 Spring Boot 应用程序上传和下载文件

验证文件是否上传到 application.properties 文件C:/Users/1302143/uploads中提到的路径

使用 Spring Boot 应用程序上传和下载文件

您可以在上图中看到图像已保存在上述路径中。

2.上传多个文件

调用 API http://localhost:8080/upload-multiple-files并上传多个文件,它可以是任何图像、pdf 或任何其他格式。

使用 Spring Boot 应用程序上传和下载文件

 

这里我上传了三个.txt、pdf、png类型的文件,让我们验证它存储在提到的路径C:/Users/1302143/uploads 上

使用 Spring Boot 应用程序上传和下载文件

伟大的!您可以在上图中看到,所有三个文件都已保存在上述位置。

3. 下载文件

转到 POSTMAN 并输入以下 API http://localhost:8080/download-file/Core_java.pdf 确保 fileName 必须正确。

而不是点击发送按钮,首先选择发送和下载,如下图所示。您的文件将被下载。

使用 Spring Boot 应用程序上传和下载文件

好吧!最后,我们构建了用于下载和上传多个文件的 RestFul API。

注意:如果您想要前端代码,请从以下链接下载源代码。

只需在eclipse工作空间下的路径src\main\resources\static下添加三个前端文件,分别是 main.css、index.html和main.js。

下载源代码。

Eclipse 中文件夹的最终结构。

使用 Spring Boot 应用程序上传和下载文件

概括

非常感谢您阅读本文。在本文中,我们学习了如何实现使用 Spring Boot 上传和下载文件的功能。如果您有任何疑问或疑问,请随时在评论框中提问。

你也可以看看我的文章。

上一篇:P6222 「P6156 简单题」加强版 题解


下一篇:【数学】数论分块