SpringBoot上传文件

本文只是对方志朋大神写过的一篇关于SpringBoot上传文件的一点补充,文章参考链接:SpringBoot非官方教程 | 第十七篇:上传文件

方志朋大神已经写的很好了,把核心代码写了出来思路已经很清晰,只是缺少一些细节,比如FileUploadController类中用到的StorageService 等对于新手来说不知道从哪里来的,上下文也没有提及。而这个类是个服务层类,不是jar里的,是自己手动创建的,下面我就把缺少的这个类给补全,完善一下:

StorageService 服务层接口

import org.springframework.core.io.Resource;
import org.springframework.web.multipart.MultipartFile;
 
import java.nio.file.Path;
import java.util.stream.Stream;
 
public interface StorageService {
 
    void init();
 
    void store(MultipartFile file);
 
    Stream<Path> loadAll();
 
    Path load(String filename);
 
    Resource loadAsResource(String filename);
 
    void deleteAll();
 
}

FileSystemStorageService 服务层实现

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

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

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.stream.Stream;
 
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.FileSystemUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;
 
@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());
	}
 
	@Override
	public void store(MultipartFile file) {
		// 格式化文件名,去掉多余的./
		String filename = StringUtils.cleanPath(file.getOriginalFilename());
		try {
			if (file.isEmpty()) {
				throw new StorageException("Failed to store empty file " + filename);
			}
			if (filename.contains("..")) {
				// This is a security check
				throw new StorageException("Cannot store file with relative path outside current directory " + filename);
			}
			// 从 file 输入流复制到目标位置
			Files.copy(file.getInputStream(), this.rootLocation.resolve(filename), StandardCopyOption.REPLACE_EXISTING);
		} catch (IOException e) {
			throw new StorageException("Failed to store file " + filename, e);
		}
	}
 
	@Override
	public Stream<Path> loadAll() {
		try {
			// 通过给定的目录和深度来遍历,返回 Stream (集合中包含给定的路径)
			// filter 过滤掉指定的路径
			// map 将路径处理为相对路径,如: rootLocation = "a/b" path = "a/b/c/img.png" relativize 后,结果为 "c/img.png"
			return Files.walk(this.rootLocation, 1).filter(path -> !path.equals(this.rootLocation)).map(path -> this.rootLocation.relativize(path));
		} catch (IOException e) {
			throw new StorageException("Failed to read stored files", e);
		}
 
	}
 
	@Override
	public Path load(String filename) {
		// 组合一个新的 Path 对象, 如: filename = "gus" rootLocation="a/b", 执行后结果为 "a/b/gus"
		return rootLocation.resolve(filename);
	}
 
	@Override
	public Resource loadAsResource(String filename) {
		try {
			Path file = load(filename);
			// file.toUri 将 Path 转换为 uri 
			// 如: path = "upload-dir/1.jpg" toUrl 后结果为 "file:///home/maiyo/project/upload-files/upload-dir/1.jpg"
			// 通过 UrlResource 创建一个 Srping Resource 对象
			Resource resource = new UrlResource(file.toUri());
			
			// 判断资源是否存在与可读
			if (resource.exists() || resource.isReadable()) {
				return resource;
			} else {
				throw new StorageFileNotFoundException("Could not read file: " + filename);
 
			}
		} catch (MalformedURLException e) {
			throw new StorageFileNotFoundException("Could not read file: " + filename, e);
		}
	}
 
	@Override
	public void deleteAll() {
		// 删除该目录下所有文件
		FileSystemUtils.deleteRecursively(rootLocation.toFile());
	}
 
	@Override
	public void init() {
		try {
			// 创建上传目录
			Files.createDirectories(rootLocation);
		} catch (IOException e) {
			throw new StorageException("Could not initialize storage", e);
		}
	}
}

StorageException 自定义异常类

//自定义异常类
public class StorageException extends RuntimeException {
 
    public StorageException(String message) {
        super(message);
    }
 
    public StorageException(String message, Throwable cause) {
        super(message, cause);
    }
}

StorageFileNotFoundException 自定义异常类,继承自 StorageException

// 自定义异常类
public class StorageFileNotFoundException extends StorageException {
 
    public StorageFileNotFoundException(String message) {
        super(message);
    }
 
    public StorageFileNotFoundException(String message, Throwable cause) {
        super(message, cause);
    }
}

属性配置

使用@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 去声明属性配置类。 像上面说的一样声明就可以正常运行了

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值