1

背景

最近在学习Spring Boot,跟着官方demo学习还是会遇到不少坑,写个文章记录下,以便日后查阅。

核心功能

使用@Controller注解声明接受请求,调用@Autowired自动注入的servcie来保存上传的文件,同时提供文件下载功能。

使用thymeleaf模板来处理html页面请求。
用@ConfigurationProperties注解来声明属性,配置文件存放目录。

核心代码

控制层

在控制器中@Controller注解来表明这是一个控制层bean,可以接受http请求;
使用@Autowired注解来自动注入service类,用来执行对文件的操作;
分别使用@GetMapping和@PostMapping来接受GET、POST方法;
通过 return "uploadForm"; 的样子,交给模板引擎thymeleaf去处理,返回template目录下的uploadForm.html文件,并“注入”响应的属性 如files、message等。

@Controller // 控制器注解
public class FileUploadController {
    private final StorageService storageService; // 下面自动注入

    @Autowired
    public FileUploadController(StorageService storageService) {
        this.storageService = storageService;
    }

    @GetMapping("/")
    public String listUploadedFiles(Model model) throws IOException {

        model.addAttribute("files", storageService.loadAll().map(
                path -> MvcUriComponentsBuilder.fromMethodName(FileUploadController.class,
                        "serveFile", path.getFileName().toString()).build().toString())
                .collect(Collectors.toList()));

        return "uploadForm"; // 由模板引擎负责返回 uploadForm.html,在template目录下。
    }

    @GetMapping("/files/{filename:.+}") // 下载文件
    @ResponseBody
    public ResponseEntity<Resource> serveFile(@PathVariable String filename) {

        Resource file = storageService.loadAsResource(filename);
        return ResponseEntity.ok().header(HttpHeaders.CONTENT_DISPOSITION,
                "attachment; filename=\"" + file.getFilename() + "\"").body(file);
    }

    @PostMapping("/")
    public String handleFileUpload(@RequestParam("file") MultipartFile file,
                                   RedirectAttributes redirectAttributes) {

        storageService.store(file);
        redirectAttributes.addFlashAttribute("message",
                "You successfully uploaded " + file.getOriginalFilename() + "!");

        return "redirect:/"; // 跳转到 get的请求里(上面的listUploadedFiles方法)
    }

}

服务层

这个类主要用来执行一些文件处理操作,没什么特殊的,

比较坑的是,因为用到配置属性,仅仅用Autowired来注入还是不行的, 还必须使用 @EnableConfigurationProperties(StorageProperties.class) 来声明配置属性类,这个比较坑。。

@Service //声明service
@EnableConfigurationProperties(StorageProperties.class) // 允许使用配置注解
public class FileSystemStorageService implements StorageService {

    private final Path rootLocation;

    @Autowired // 自动注入
    public FileSystemStorageService(StorageProperties properties) {
        this.rootLocation = Paths.get(properties.getLocation());
    }

属性配置

使用@ConfigurationProperties声明这是一个属性配置类,参数声明顶级命名空间。

比较坑的是, 需要在pom.xml里引入以下依赖:

    <dependency>
            <groupId> org.springframework.boot </groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <optional> true </optional>
        </dependency>

然后就可以正常使用了。。。

@ConfigurationProperties("storage")
public class StorageProperties {

    /**
     * Folder location for storing files
     */
    private String location = "upload-dir";

    public String getLocation() {
        return location;
    }

    public void setLocation(String location) {
        this.location = location;
    }

}

以上配置完成后,可以在application.properties 文件里添加 storage.location = myFiles来指明文件存储路径。。。

其它坑

在学习的过程中可能会出现 “Re-run spring boot configuration annotation”的错误,这个就是因为在service中只用Autowired去自动注入了,而没有使用 EnableConfigurationProperties 去声明属性配置类。 像上面说的一样声明就可以正常运行了

参考链接


紫日残月
239 声望8 粉丝