项目背景:
接入私有OSS以后,需要实现图片上传/访问,但因仓库私有,也不像ali-oss等云服务可以提供生成临时共有访问地址,只能以流的方式从OSS读取数据并渲染到前端。
引入依赖:
1 2 3 4 5 6 7
| <dependency> <groupId>com.amazonaws</groupId> <artifactId>aws-java-sdk-s3</artifactId> <version>1.11.490</version> </dependency>
|
代码实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72
| package com.hsyk.bright.client.controller;
import cn.dev33.satoken.util.SaResult; import com.hsyk.bright.client.utils.AwsS3Component; import io.swagger.annotations.Api; import org.apache.commons.lang3.StringUtils; import org.springframework.util.AntPathMatcher; import org.springframework.web.bind.annotation.*; import org.springframework.web.multipart.MultipartFile; import org.springframework.web.servlet.HandlerMapping;
import javax.annotation.Resource; import javax.imageio.ImageIO; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.swing.*; import java.awt.*; import java.awt.image.BufferedImage; import java.io.*;
@RestController @RequestMapping("/aws") @Api(tags = "aws文件上传") public class AwsOssController {
@Resource AwsS3Component awsS3Component;
@Resource HttpServletResponse response;
@PostMapping("/file/upload") public SaResult upload(MultipartFile file,String folder) throws Exception { return SaResult.data(awsS3Component.upload(file,folder)); }
@GetMapping("/get/{address}/**") public void get(@PathVariable("address") String address, HttpServletRequest request) throws IOException { final String pathQ = request.getAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE).toString(); final String bestMatchingPattern = request.getAttribute(HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE).toString(); String arguments = new AntPathMatcher().extractPathWithinPattern(bestMatchingPattern, pathQ); String moduleName; if (StringUtils.isNotBlank(arguments)) { moduleName = address + '/' + arguments; } else { moduleName = address; } String[] split = moduleName.split("\\."); String extend = split[1]; OutputStream os = null; try { InputStream inputStream = null; inputStream = awsS3Component.getInputStream(moduleName); BufferedImage image = ImageIO.read(inputStream); response.setContentType("image/" + extend); os = response.getOutputStream(); if(image != null){ ImageIO.write(image,extend,os); } } catch (IOException e) { e.printStackTrace(); }finally { if(os != null){ os.flush(); os.close(); } } }
}
|
注意 response.setContentType(“image/“ + extend); 必须设置返回的文件类型,浏览器才可以直接渲染展示
用到了工具类AwsS3Component和FileMd5Util
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120
| package com.hsyk.bright.client.utils;
import com.amazonaws.ClientConfiguration; import com.amazonaws.Protocol; import com.amazonaws.auth.AWSStaticCredentialsProvider; import com.amazonaws.auth.BasicAWSCredentials; import com.amazonaws.client.builder.AwsClientBuilder; import com.amazonaws.regions.Regions; import com.amazonaws.services.s3.AmazonS3; import com.amazonaws.services.s3.AmazonS3ClientBuilder; import com.amazonaws.services.s3.model.*; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.annotation.Value; import org.springframework.cloud.context.config.annotation.RefreshScope; import org.springframework.stereotype.Component; import org.springframework.web.multipart.MultipartFile;
import javax.annotation.Resource; import javax.imageio.ImageIO; import javax.swing.*; import java.awt.*; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.net.URL; import java.util.Date; import java.util.concurrent.TimeUnit;
@Component @Slf4j @RefreshScope public class AwsS3Component implements InitializingBean {
@Value("${aws.accessKey}") private String accessKey; @Value("${aws.secretKey}") private String accessSecret; @Value("${aws.bucket}") private String bucket; @Value("${aws.endpoint}") private String endpoint;
private AmazonS3 client;
@Resource private FileMd5Util fileMd5Util;
@Override public void afterPropertiesSet() throws Exception { ClientConfiguration config = new ClientConfiguration(); config.setProtocol(Protocol.HTTP); config.disableSocketProxy(); this.client = AmazonS3ClientBuilder .standard() .withClientConfiguration(config) .withCredentials(new AWSStaticCredentialsProvider(new BasicAWSCredentials(accessKey, accessSecret))) .withEndpointConfiguration(new AwsClientBuilder.EndpointConfiguration(endpoint, Regions.CN_NORTH_1.getName())) .enablePathStyleAccess() .build(); }
public String upload(File file, String key) { client.putObject(new PutObjectRequest(bucket, key, file).withCannedAcl(CannedAccessControlList.PublicRead)); GeneratePresignedUrlRequest urlRequest = new GeneratePresignedUrlRequest(bucket, key); URL url = client.generatePresignedUrl(urlRequest); return url.toString(); }
public String upload(MultipartFile file,String folder) throws IOException { String fileMd5 = fileMd5Util.getFileMd5Value(file); String fileName = file.getOriginalFilename(); String prefix = fileName.substring(fileName.lastIndexOf(".") + 1); StringBuffer tempFileName = new StringBuffer(fileMd5); tempFileName.append(".").append(prefix); String localFileName = tempFileName.toString(); ObjectMetadata metadata = new ObjectMetadata(); metadata.setContentLength(file.getInputStream().available()); localFileName = folder + "/" + localFileName; client.putObject(new PutObjectRequest(bucket, localFileName, file.getInputStream(), metadata).withCannedAcl(CannedAccessControlList.PublicRead)); GeneratePresignedUrlRequest urlRequest = new GeneratePresignedUrlRequest(bucket, localFileName); URL generatePresignedUrl = client.generatePresignedUrl(urlRequest); log.info("generatePresignedUrl========================={}",generatePresignedUrl); String url = endpoint + "/"+ bucket + "/" + localFileName; log.info("文件---[{}]---上传到S3服务器成功!",url); return localFileName; }
public InputStream getInputStream(String url){ S3Object s3Object = client.getObject(new GetObjectRequest(bucket, url)); InputStream inputStream = s3Object.getObjectContent(); return inputStream; }
}
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
| package com.hsyk.bright.client.utils;
import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; import org.springframework.web.multipart.MultipartFile;
import java.io.IOException; import java.io.InputStream; import java.security.MessageDigest;
@Slf4j @Component public class FileMd5Util {
public String getFileMd5Value(MultipartFile multipartFile) { InputStream in = null; try { in = multipartFile.getInputStream(); byte[] buffer = new byte[2048]; MessageDigest digest = MessageDigest.getInstance("MD5"); while (true) { int len = in.read(buffer, 0, 2048); if (len != -1) { digest.update(buffer, 0, len); } else { break; } } in.close(); byte[] md5Bytes = digest.digest(); StringBuffer hexValue = new StringBuffer(); for (int i = 0; i < md5Bytes.length; i++) { int val = ((int) md5Bytes[i]) & 0xff; if (val < 16) { hexValue.append("0"); } hexValue.append(Integer.toHexString(val)); } return hexValue.toString(); } catch (Exception e) { log.error("获取文件md5失败"); } finally { try { in.close();
} catch (IOException ex) { log.error("关闭流异常", ex); } } return null; }
}
|
在渲染图片时需自定义配置注入BufferedImageHttpMessageConverter
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| package com.hsyk.bright.client.config;
import org.springframework.context.annotation.Configuration; import org.springframework.http.converter.BufferedImageHttpMessageConverter; import org.springframework.http.converter.HttpMessageConverter; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import java.util.List;
@Configuration public class WebMvcConfig implements WebMvcConfigurer {
@Override public void extendMessageConverters(List<HttpMessageConverter<?>> converters) { converters.add(new BufferedImageHttpMessageConverter()); } }
|
在使用@PathVariable注解接受地址栏携带”/“的参数时需注意以下配置
请求地址示例:
1
| https://xxxxxxxx.com/aws/get/image/fx/fb9265fa192edc8cac0daff888c91f9d.jpeg
|
其中image/fx/fb9265fa192edc8cac0daff888c91f9d.jpeg支持桶下多级目录
在配置请求地址时参数后面加上/**
1
| @GetMapping("/get/{address}/**")
|
在获取地址参数时使用以下方法
1 2 3 4 5 6 7 8 9 10 11
| final String pathQ = request.getAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE).toString(); final String bestMatchingPattern = request.getAttribute(HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE).toString(); String arguments = new AntPathMatcher().extractPathWithinPattern(bestMatchingPattern, pathQ);
String moduleName; if (StringUtils.isNotBlank(arguments)) { moduleName = address + '/' + arguments; } else { moduleName = address; }
|