Java Springboot图片上传(文件上传)和回显

在Springboot中如果需要上传文件或者图片非常简单,只需要编写一个工具类来保存用户上传的图片或者是文件即可。在这里通过一个服务接口和实现类来完成文件的上传。

可以将图片的上传地址配置在application.yml中,如下所示,

# 自定义存储位置@ConfigurationProperties
file-upload:
  upload-dir: public
package com.x.x.config;

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

// 需要让注解生效需要在启动类中配置
@ConfigurationProperties(prefix = "file-upload")
public class FileStorageProperties {
    private String uploadDir;

    public String getUploadDir() {
        return uploadDir;
    }

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

需要注解生效需要在启动类中进行导入,如下代码所示。

//配置配置导入
@EnableConfigurationProperties({
        FileStorageProperties.class
})

这样就可以在任何地方使用注解

首先是在Service中创建一个服务接口。

import org.springframework.core.io.Resource;
import org.springframework.web.multipart.MultipartFile;

/**
 * 文件上传实现接口
 */
public interface StorageService {
    String storeFile(MultipartFile file);

    Resource loadFileAsResource(String fileName);
}

在impl文件夹中对这个接口进行实现。如下代码所示。

package com.x.x.service.impl;

import com.x.x.config.FileStorageProperties;
import com.x.x.exception.FileStorageException;
import com.x.x.service.StorageService;
import com.x.x.util.UUIDHexGenerator;
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;

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 java.util.Objects;

@Service
public class StorageServiceImpl implements StorageService {
    private final Path fileStorageLocation;

    @Autowired
    public StorageServiceImpl(FileStorageProperties fileStorageProperties) {
        this.fileStorageLocation = Paths.get(fileStorageProperties.getUploadDir()).toAbsolutePath().normalize();

        try {
            Files.createDirectories(this.fileStorageLocation);
        } catch (Exception ex) {
            throw new FileStorageException("Could not create directory where the uploaded files will be stored");
        }
    }

    @Override
    public String storeFile(MultipartFile file) {
        String fileName = StringUtils.cleanPath(Objects.requireNonNull(file.getOriginalFilename()));

        try {
            if (fileName.contains("..")) {
                throw new FileStorageException("File name contains invalid path sequence");
            }
            // 生成唯一的文件名称
            String uploadFileName= UUIDHexGenerator.generate()+fileName;
            Path targetLocation = this.fileStorageLocation.resolve(uploadFileName);
            Files.copy(file.getInputStream(), targetLocation, StandardCopyOption.REPLACE_EXISTING);
            return uploadFileName;
        } catch (IOException ex) {
            throw new FileStorageException("Could not store file " + fileName + ". Please try again later!", ex);
        }
    }

    @Override
    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 FileStorageException("File not found" + fileName);
            }
        } catch (MalformedURLException ex) {
            throw new FileStorageException("File not found" + fileName);
        }
    }
}

在Controller中使用服务

package com.x.wx_qjh.controller;


import com.x.x.model.UploadFileResponse;
import com.x.wx_qjh.service.StorageService;
import com.x.wx_qjh.util.OperationRecord;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
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.*;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.support.ServletUriComponentsBuilder;

import javax.servlet.http.HttpServletRequest;
import java.io.IOException;

/**
 * <p>
 * 前端控制器
 * </p>
 *
 * @author stiller
 * @since 2021-05-02
 */
@RestController
@RequestMapping("/file-controller")
public class FileController {

    private final StorageService storageService;
    private final OperationRecord op = new OperationRecord();


    public FileController(StorageService storageService) {
        this.storageService = storageService;
    }

    /**
     * 单一文件上传
     * @param file
     * @return
     * 小程序特殊性所以只返回地址
     */
    @PostMapping("/uploadFile")
    public String uploadFile(@RequestParam("file") MultipartFile file) {
        String fileName = storageService.storeFile(file);

        String fileDownloadUri = ServletUriComponentsBuilder
                .fromCurrentContextPath()
                .path("/upload/")
                .path(fileName).toUriString();

        UploadFileResponse ufr = new UploadFileResponse(fileName, fileDownloadUri, file.getContentType(), file.getSize());

        return ufr.getFileDownloadUri();
    }

//    /**
//     * 循环调用批量上传
//     * @param files
//     * @return
//     */
//    @PostMapping("/uploadMultipleFiles")
//    public List<ResultInfo> uploadMultipleFiles(@RequestParam("files") MultipartFile[] files) {
//        return Arrays.stream(files).map(this::uploadFile).collect(Collectors.toList());
//    }

    @GetMapping("/downloadFile/{fileName:.+}")
    public ResponseEntity<Resource> downloadFile(@PathVariable String fileName, HttpServletRequest request) {
        Resource resource = storageService.loadFileAsResource(fileName);

        String contentType = null;
        try {
            contentType = request.getServletContext().getMimeType(resource.getFile().getAbsolutePath());
        } catch (IOException ex) {
            op.recode("Could not determine file type");
        }

        if (contentType == null) {
            contentType = "application/octet-stream";
        }
        return ResponseEntity.ok().contentType(MediaType.parseMediaType(contentType))
                .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + resource.getFilename() + "\"")
                .body(resource);
    }
}

使用到的Model类

package com.x.x.model;

import lombok.Data;

@Data
public class UploadFileResponse {

    private String fileName;

    private String fileDownloadUri;

    private String fileType;

    private long fileSize;

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

}

比较麻烦是图片的回显问题,如果需要将图片直接显示给前台,可以编写一个截拦器进行地址的映射,将静态文件映射为真实的地址,如下代码所示。

package com.x.x.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class FileShowResourceInterceptor implements WebMvcConfigurer {

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/static/**").addResourceLocations("classpath:/static/");
        registry.addResourceHandler("/upload/**").addResourceLocations("file:D:/file/java/x/public/");
    }
}

链接