zoukankan      html  css  js  c++  java
  • Spring Boot 实现文件上传

    在实际项目中,文件上传是很多项目必不可少的一个功能。那么在 Spring Boot 项目中又是如何来实现文件上传功能的呢?一般来说,上传的文件可以保存到项目根目录下的某一文件夹中,但这样做显然是不太合适的。因此我们选择将文件上传到专门的文件服务器中。很多云计算厂商都提供文件存储服务。这里我选择的是阿里云的对象存储(OSS)。

    一、配置OSS

    1. 导入SDK

    首先,你需要注册阿里云的账号并开通对象存储服务。在准备工作完成之后,需要导入 JAVA 版本的 SDK,这里使用 maven 进行导入

    <!-- 阿里云OSS对象存储 -->
    <dependency>
    	<groupId>com.aliyun.oss</groupId>
    	<artifactId>aliyun-sdk-oss</artifactId>
    	<version>3.8.0</version>
    </dependency>
    
    2. 修改配置文件

    导入完成后在 application.properties 配置文件中添加以下内容

    # 节点域名
    aliyun.oss.endpoint=oss-cn-xxxxxxx.aliyuncs.com
    # 账户id
    aliyun.oss.accessKeyId=xxxxxxxxxxxxx
    # 账户密码
    aliyun.oss.accessKeySecret=xxxxxxxxxxxxx
    # bucket名称
    aliyun.oss.bucketName=xxxxxxxxxxx
    # 签名过期时间
    aliyun.oss.policy.expire=300
    # 上传文件的最大尺寸
    aliyun.oss.maxSize=10
    # 上传地址的前缀
    aliyun.oss.dir.prefix=xxx
    # 回调参数的请求地址
    aliyun.oss.callback=http://www.xxxxxx.com/api/aliyun/oss/callback
    

    以上内容在开通服务后均可获取到,请根据实际情况进行修改

    3. 初始化

    OSSClient是OSS的Java客户端,用于管理存储空间和文件等OSS资源。使用Java SDK发起OSS请求,您需要初始化一个OSSClient实例,并根据需要修改ClientConfiguration的默认配置项。

    根据官方文档的描述,需要初始化一个ossClient实例并将其注入到Spring容器中,因此可以编写一个配置类OssConfig

    @Configuration
    @PropertySource(value = {"classpath:application.properties"}, encoding = "utf-8")
    public class OssConfig {
    
        @Value("${aliyun.oss.endpoint}")
        private String endpoint;
    
        @Value("${aliyun.oss.accessKeyId}")
        private String accessKeyId;
    
        @Value("${aliyun.oss.accessKeySecret}")
        private String secretAccessKey;
    
        @Bean
        public OSS ossClient(){
            return new OSSClientBuilder().build(endpoint, accessKeyId, secretAccessKey);
        }
    }
    

    更多详细的配置,请参考官方文档:初始化

    二、文件上传

    1. 流程分析

    我们以典型的表单上传为例,在使用对象存储OSS后,表单上传分为以下几个流程:

    注:Policy表单域用于验证请求的合法性。例如可以指定上传的大小,可以指定上传的Object名称等,上传成功后客户端跳转到的URL,上传成功后客户端收到的状态码。

    PolicyConditions policyConds = new PolicyConditions();
    policyConds.addConditionItem(PolicyConditions.COND_CONTENT_LENGTH_RANGE, 0, maxSize);
    policyConds.addConditionItem(MatchMode.StartWith, PolicyConditions.COND_KEY, DIR_PREFIX);
    String postPolicy = ossClient.generatePostPolicy(expiration, policyConds);
    byte[] binaryData = postPolicy.getBytes(StandardCharsets.UTF_8);
    // 将Policy字符串进行base64编码
    String policy = BinaryUtil.toBase64String(binaryData);
    // 用OSS的AccessKeySecret对base64编码后的Policy进行签名
    String signature = ossClient.calculatePostSignature(postPolicy);
    

    前端向OSS服务器上传文件时要上传Policy表单域,OSS服务器将对Policy表单域的内容进行验证。关于 Post Policy 的详细内容,请参考官方文档:Post Policy

    当文件上传成功后,OSS服务器会向应用服务器发起回调请求,具体流程如下:

    用户只需要在发送给 OSS 的请求中携带相应的 Callback 参数,即能实现回调。

    Callback 参数是由一段经过 base64 编码的 JSON 字符串(字段)。构建 callback 参数的关键是指定请求回调的服务器 URL(callbackUrl)以及回调的内容(callbackBody)。

    // 上传回调参数
    Callback callback = new Callback();
    // 指定请求回调的服务器URL
    callback.setCallbackUrl(CALLBACK);
    //(可选)设置回调请求消息头中Host的值,即您的服务器配置Host的值。
    // callback.setCallbackHost("yourCallbackHost");
    // 设置发起回调时请求body的值。
    callback.setCallbackBody("{\"filename\":${object},\"mineType\":${mimeType}}");
    // 设置发起回调请求的Content-Type。
    callback.setCalbackBodyType(Callback.CalbackBodyType.JSON);
    // 设置发起回调请求的自定义参数,由Key和Value组成,Key必须以x:开始。
    // callback.addCallbackVar("x:dir", "value");
    

    更详细的内容请阅读官方文档:Callback

    2. 功能实现

    首先编写 Post Policy 封装对象OssPolicyResult

    @Data
    public class OssPolicyResult {
    
    	@ApiModelProperty("用户id")
    	private String accessKeyId;
    	
    	@ApiModelProperty("Post Policy经过base64编码过的字符串")
            private String policy;
    	
    	@ApiModelProperty("对policy签名后的字符串")
            private String signature;
    
    	// @ApiModelProperty("对象的键值")
    	// private String key;
    	
    	@ApiModelProperty("上传文件夹路径前缀")
            private String dir;
    	
    	@ApiModelProperty("oss对外服务的访问域名")
            private String host;
    
    	@ApiModelProperty("上传成功后的回调设置")
    	private String callback;
    }
    

    然后需自定义一个回调结果对象OssCallBackResult

    @Data
    public class OssCallBackResult {
    
        @ApiModelProperty("文件的链接")
        private String url;
        @ApiModelProperty("文件名称")
        private String filename;
        @ApiModelProperty("文件大小")
        private String size;
        @ApiModelProperty("文件的mimeType")
        private String  mimeType;
        @ApiModelProperty("图片文件的宽")
        private String width;
        @ApiModelProperty("图片文件的高")
        private String height;
    }
    
    

    注:以上内容可根据实际需要进行修改

    之后编写 Service 接口及实现类

    Service 接口:

    public interface OssService {
    	
            // 生成Post Policy
    	OssPolicyResult policy();
    	
            // 上传成功后的回调
    	OssCallBackResult callback(Map<String, Object> requestBody);
    }
    

    Service 实现类:

    @Slf4j
    @Service
    @PropertySource(value = {"classpath:application.properties"}, encoding = "utf-8")
    public class OssServiceImpl implements OssService {
    
    	@Value("${aliyun.oss.endpoint}")
    	private String ENDPOINT;
    
    	@Value("${aliyun.oss.accessKeyId}")
    	private String ACCESS_KEY_ID;
    
    	@Value("${aliyun.oss.accessKeySecret}")
    	private String SECRET_ACCESS_KEY;
    
    	@Value("${aliyun.oss.bucketName}")
    	private String BUCKET_NAME;
    
    	@Value("${aliyun.oss.policy.expire}")
    	private int EXPIRE;
    
    	@Value("${aliyun.oss.maxSize}")
    	private int MAX_SIZE;
    
    	@Value("${aliyun.oss.dir.prefix}")
    	private String DIR_PREFIX;
    
    	@Value("${aliyun.oss.callback}")
    	private String CALLBACK;
    
    	@Autowired
    	private OSS ossClient;
    
    	@Override
    	public OssPolicyResult policy() {
    		OssPolicyResult result = new OssPolicyResult();
    		// 签名有效期
    		long expireEndTime = System.currentTimeMillis() + EXPIRE * 1000;
    		Date expiration = new Date(expireEndTime);
    		// 文件名称
    		// String filename = UUID.randomUUID().toString();
    		// 文件大小
    		long maxSize = MAX_SIZE * 1024 * 1024;
    		// 提交节点
    		String action = "http://" + BUCKET_NAME + "." + ENDPOINT;
    		// 上传回调参数
    		Callback callback = new Callback();
    		// 指定请求回调的服务器URL
    		callback.setCallbackUrl(CALLBACK);
    		//(可选)设置回调请求消息头中Host的值,即您的服务器配置Host的值。
    		// callback.setCallbackHost("yourCallbackHost");
    		// 设置发起回调时请求body的值。
    		callback.setCallbackBody("{\"filename\":${object}}");
    		// 设置发起回调请求的Content-Type。
    		callback.setCalbackBodyType(Callback.CalbackBodyType.JSON);
    		// 设置发起回调请求的自定义参数,由Key和Value组成,Key必须以x:开始。
    		// callback.addCallbackVar("x:dir", "value");
    		try {
    			PolicyConditions policyConds = new PolicyConditions();
    			policyConds.addConditionItem(PolicyConditions.COND_CONTENT_LENGTH_RANGE, 0, maxSize);
    			policyConds.addConditionItem(MatchMode.StartWith, PolicyConditions.COND_KEY, DIR_PREFIX);
    			String postPolicy = ossClient.generatePostPolicy(expiration, policyConds);
    			byte[] binaryData = postPolicy.getBytes(StandardCharsets.UTF_8);
                            // 将Policy字符串进行base64编码
    			String policy = BinaryUtil.toBase64String(binaryData);
                            // 用OSS的AccessKeySecret对base64编码后的Policy进行签名
    			String signature = ossClient.calculatePostSignature(postPolicy);
                            // 将callback配置进行base64编码
    			String callbackData = BinaryUtil.toBase64String(OSSUtils.jsonizeCallback(callback).getBytes());
    			// 返回结果
    			result.setAccessKeyId(ACCESS_KEY_ID);
    			result.setPolicy(policy);
    			result.setSignature(signature);
    			// result.setKey(filename);
    			result.setDir(dir);
    			result.setHost(action);
    			result.setCallback(callbackData);
    		} catch (Exception e) {
    			log.error("签名生成失败", e);
    		}
    		return result;
    	}
    
    	@Override
    	public OssCallBackResult callback(Map<String, Object> requestBody) {
    		OssCallBackResult ossCallbackResult = new OssCallBackResult();
    		// 文件名
    		String filename = requestBody.get("filename").toString();
    		// 文件链接
    		String url = "https://" + BUCKET_NAME + "." + ENDPOINT + "/" + DIR_PREFIX + "/" + filename;
    		ossCallbackResult.setUrl(url);
    		return ossCallbackResult;
    	}
    }
    

    添加 Controller 层:

    @Api(tags = "阿里云对象存储接口")
    @RequestMapping("/api")
    @RestController
    public class OssController {
    	
    	@Autowired
    	private OssService ossService;
    	
    	@ApiOperation(value = "OSS上传签名生成")
    	@GetMapping("/aliyun/oss/policy")
    	public Object policy() {
    		return ossService.policy();
    	}
    
    	@ApiOperation(value = "OSS上传成功回调")
    	@PostMapping("/aliyun/oss/callback")
    	public Object callback(@RequestBody Map<String, Object> requestBody) {
    		return ossService.callback(requestBody);
    	}
    }
    
  • 相关阅读:
    伪元素:placeholder-shown&&:focus-within
    伪元素:target
    伪元素:focus-within
    MpVue解析
    ESLint在vue中的使用
    vue动态 设置类名
    Java 文件流操作.
    SpringMVC 与 REST.
    基于Nginx和Zookeeper实现Dubbo的分布式服务
    基于Spring的RPC通讯模型.
  • 原文地址:https://www.cnblogs.com/fzcoder/p/14087651.html
Copyright © 2011-2022 走看看