背景
官方文檔
一,s3 sdk 依賴
<dependency>
<groupId>com.amazonaws</groupId>
<artifactId>aws-java-sdk</artifactId>
<version>1.11.126</version>
</dependency>
二,上傳工具類
package test.s3;
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 org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import java.io.InputStream;
import java.net.URL;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
public class AmazonS3Util {
/**
* S3伺服器位址
*/
private final static String S3_URL = "";
/**
* 賬号key
*/
private final static String ACCESS_KEY = "";
/**
* 密碼key
*/
private final static String SECRET_KEY = "";
/**
* 存放合并資訊集合 如果服務是叢集或多活部署,需改成redis存儲
*/
private final static Map<String, Map<String, Object>> uploadIdMap = new ConcurrentHashMap<>();
private static Logger logger = LoggerFactory.getLogger(AmazonS3Util.class);
/**
* 擷取連接配接
* @return
* @throws Exception
*/
public static AmazonS3 connectS3Client() throws Exception {
URL url = new URL(S3_URL);
ClientConfiguration config = new ClientConfiguration().withProtocol(Protocol.HTTP);
AmazonS3 client = AmazonS3ClientBuilder.standard()
.withEndpointConfiguration(new AwsClientBuilder.EndpointConfiguration(url.toString(),Regions.CN_NORTH_1.toString()))
.withClientConfiguration(config)
.withPathStyleAccessEnabled(true)
.withCredentials(new AWSStaticCredentialsProvider(new BasicAWSCredentials(ACCESS_KEY, SECRET_KEY)))
.build();
return client;
}
/**
* 上傳檔案
* @param upload
* @return
* @throws Exception
*/
public static void uploadFile(Upload upload) throws Exception {
AmazonS3 s3client = connectS3Client();
InputStream inputStream = null;
try {
String fileKey = getFileKey(upload.getMultipartFile().getOriginalFilename());
inputStream = upload.getMultipartFile().getInputStream();
PutObjectRequest request = new PutObjectRequest(upload.getBucketName(), fileKey, inputStream,null);
s3client.putObject(request);
} catch (Exception e) {
throw e;
} finally {
if (inputStream != null) {
try {
inputStream.close();
} catch (Exception e) {
throw e;
}
}
}
}
/**
* 分段上傳檔案
* @param upload
* @return
* @throws Exception
*/
public static String mutilpartUpload(Upload upload) throws Exception {
logger.info("開始上傳");
AmazonS3 s3client = connectS3Client();
InputStream inputStream = null;
try {
String fileKey = null;
String uploadId = null;
List<PartETag> partETagList = new ArrayList<>();
Map<String, Object> result = uploadIdMap.get(upload.getIdentifier());
if (result == null) {
fileKey = getFileKey(upload.getMultipartFile().getOriginalFilename());
InitiateMultipartUploadRequest initRequest = new InitiateMultipartUploadRequest(upload.getBucketName(), fileKey);
InitiateMultipartUploadResult initResponse = s3client.initiateMultipartUpload(initRequest);
uploadId = initResponse.getUploadId();
result = new HashMap<>();
result.put("uploadId", uploadId);
result.put("partETagList", partETagList);
result.put("fileKey", fileKey);
} else {
uploadId = result.get("uploadId").toString();
partETagList = (List<PartETag>) result.get("partETagList");
fileKey = result.get("fileKey").toString();
}
// 分段上傳
inputStream = upload.getMultipartFile().getInputStream();
UploadPartRequest uploadRequest = new UploadPartRequest()
.withBucketName(upload.getBucketName())
.withKey(fileKey)
.withUploadId(uploadId)
.withPartNumber(upload.getChunkNumber())
.withInputStream(inputStream)
.withPartSize(upload.getMultipartFile().getSize());
UploadPartResult uploadResult = s3client.uploadPart(uploadRequest);
partETagList.add(uploadResult.getPartETag());
logger.info("上傳完成: upload.getTotalChunks():{}, upload.getChunkNumber():{}", upload.getTotalChunks(), upload.getChunkNumber());
if (upload.getTotalChunks() == upload.getChunkNumber()) {
logger.info("開始合并");
CompleteMultipartUploadRequest compRequest = new CompleteMultipartUploadRequest(upload.getBucketName(), fileKey, uploadId, partETagList);
s3client.completeMultipartUpload(compRequest);
// 删除map資料
uploadIdMap.remove(upload.getIdentifier());
} else {
// 更新map資料
uploadIdMap.put(upload.getIdentifier(), result);
}
return fileKey;
} catch (Exception e) {
throw e;
} finally {
if (inputStream != null) {
try {
inputStream.close();
} catch (Exception e) {
throw e;
}
}
}
}
/**
* 下載下傳
* @param bucketName
* @param fileKey
* @return
* @throws Exception
*/
public static InputStream downloadFile(String bucketName, String fileKey) throws Exception{
AmazonS3 s3client = connectS3Client();
GetObjectRequest request = new GetObjectRequest(bucketName, fileKey);
S3Object response = s3client.getObject(request);
InputStream input = response.getObjectContent();
return input;
}
/**
* 删除檔案
* @param bucketName
* @param fileKey
* @throws Exception
*/
public static void deleteFile(String bucketName, String fileKey) throws Exception {
AmazonS3 s3client = connectS3Client();
DeleteObjectRequest request = new DeleteObjectRequest(bucketName, fileKey);
s3client.deleteObject(request);
}
/**
* Bucket清單
* @return
* @throws Exception
*/
public static List<Bucket> listFile() throws Exception{
AmazonS3 s3client = connectS3Client();
ListBucketsRequest request = new ListBucketsRequest();
List<Bucket> list = s3client.listBuckets(request);
return list;
}
/**
* 是否存在Bucket
* @param bucketName
* @return
* @throws Exception
*/
public static boolean isExistBucket(String bucketName) throws Exception{
AmazonS3 s3client = connectS3Client();
try {
HeadBucketRequest request = new HeadBucketRequest(bucketName);
s3client.headBucket(request);
} catch (Exception e) {
return false;
}
return true;
}
/**
* 建立Bucket
* @param bucketName
* @return
* @throws Exception
*/
public static void createBucket(String bucketName) throws Exception{
boolean isBucketExists = isExistBucket(bucketName);
AmazonS3 s3client = null;
if (!isBucketExists) {
s3client = connectS3Client();
CreateBucketRequest request = new CreateBucketRequest(bucketName);
s3client.createBucket(request);
}
}
/**
* 删除Bucket
* @param bucketName
* @return
* @throws Exception
*/
public static void deleteBucket(String bucketName) throws Exception {
AmazonS3 s3client = connectS3Client();
DeleteBucketRequest request = new DeleteBucketRequest(bucketName);
s3client.deleteBucket(request);
}
/**
* fileKey是否存在
* @param bucketName
* @param fileKey
* @return
* @throws Exception
*/
public static boolean isExistFileKey(String bucketName, String fileKey) throws Exception{
AmazonS3 s3client = connectS3Client();
GetObjectRequest request = new GetObjectRequest(bucketName,fileKey);
S3Object response = s3client.getObject(request);
boolean result = response != null && fileKey.equals(response.getKey());
return result;
}
/**
* 擷取檔案key
* @param fileName
* @return
* @throws Exception
*/
private static String getFileKey(String fileName) throws Exception{
String uuid = UUID.randomUUID().toString().replaceAll("-","");
String dateDir = new SimpleDateFormat("yyyy-MM-dd").format(new Date());
String fileKey = dateDir + "/" + uuid + "/" + fileName;
return fileKey;
}
}
封裝參數實體類
package test.s3;
import org.springframework.web.multipart.MultipartFile;
public class Upload {
/**
* 桶名稱
*/
private String bucketName;
/**
* 總塊數
*/
private int totalChunks;
/**
* 目前塊數,從1開始
*/
private int chunkNumber;
/**
* 上傳id,每個檔案唯一
*/
private String identifier;
/**
* 上傳檔案資訊, 需手動指派
*/
private MultipartFile multipartFile;
public int getTotalChunks() {
return totalChunks;
}
public String getBucketName() {
return bucketName;
}
public void setBucketName(String bucketName) {
this.bucketName = bucketName;
}
public void setTotalChunks(int totalChunks) {
this.totalChunks = totalChunks;
}
public int getChunkNumber() {
return chunkNumber;
}
public void setChunkNumber(int chunkNumber) {
this.chunkNumber = chunkNumber;
}
public String getIdentifier() { return identifier; }
public void setIdentifier(String identifier) { this.identifier = identifier; }
public MultipartFile getMultipartFile() {
return multipartFile;
}
public void setMultipartFile(MultipartFile multipartFile) { this.multipartFile = multipartFile; }
}
控制類
package test.s3;
import io.swagger.annotations.ApiOperation;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.InputStream;
@RestController
@RequestMapping("/s3")
public class FileS3Controller {
@ApiOperation("上傳")
@RequestMapping(method = RequestMethod.POST, value = "/upload")
public String upload(Upload upload, MultipartFile file) throws Exception {
upload.setMultipartFile(file);
return AmazonS3Util.mutilpartUpload(upload);
}
@ApiOperation("下載下傳")
@RequestMapping(method = RequestMethod.GET, value = "/download")
public void download(String bucketName, String fileKey, String fileName, HttpServletResponse response) throws Exception {
InputStream inputStream = AmazonS3Util.downloadFile(bucketName, fileKey);
response.setContentType( "application/x-msdownload");
fileName = new String(fileName.getBytes("gbk"), "iso8859-1");
response.setHeader("Content-Disposition", "attachment;filename=" + fileName);
ServletOutputStream out = response.getOutputStream();
byte[] bffer = new byte[1024];
int r = 0;
while ((r = inputStream.read(bffer, 0, 1024)) != -1) {
out.write(bffer, 0, r);
}
try {
inputStream.close();
} catch (Exception e) {
e.printStackTrace();
}
try {
out.close();
} catch (Exception e) {
e.printStackTrace();
}
}
@ApiOperation("删除")
@RequestMapping(value = "/delete", method = RequestMethod.GET)
public String delete(String bucketName, String fileKey) throws Exception {
AmazonS3Util.deleteFile(bucketName, fileKey);
return "删除成功";
}
}
前端
官方文檔
一,元件
npm install vue-simple-uploader --save
二,main.js
import Vue from 'vue'
import App from './App.vue'
import uploader from 'vue-simple-uploader'
Vue.config.productionTip = false
Vue.use(uploader)
new Vue({
render: h => h(App),
}).$mount('#app')
三,App.vue
<template>
<uploader :options="options" class="uploader-example">
<uploader-unsupport></uploader-unsupport>
<uploader-drop>
<p>Drop files here to upload or</p>
<uploader-btn>select files</uploader-btn>
<uploader-btn :attrs="attrs">select images</uploader-btn>
<uploader-btn :directory="true">select folder</uploader-btn>
</uploader-drop>
<uploader-list></uploader-list>
</uploader>
</template>
<script>
export default {
data () {
return {
options: {
target: '//localhost:8082/s3/upload',
query: { // 格外參數
bucketName: 'test'
},
chunkSize: 10*1024*1024, // 分塊大小
forceChunkSize: true, // 是否強制所有的塊都是小于等于 chunkSize 的值。預設是 false
simultaneousUploads: 1, // 并發上傳數,預設 3
maxChunkRetries: 3, // 最大自動失敗重試上傳次數,值可以是任意正整數,如果是 undefined 則代表無限次,預設 0
testChunks: false
},
attrs: {
accept: 'image/*'
}
}
}
}
</script>
<style>
.uploader-example {
width: 880px;
padding: 15px;
margin: 40px auto 0;
font-size: 12px;
box-shadow: 0 0 10px rgba(0, 0, 0, .4);
}
.uploader-example .uploader-btn {
margin-right: 4px;
}
.uploader-example .uploader-list {
max-height: 440px;
overflow: auto;
overflow-x: hidden;
overflow-y: auto;
}
</style>
效果
注意事項
1、以上代碼不支援并發上傳。并發上傳後續再研究解決
2、如果服務是多活部署(叢集部署),AmazonS3Util 工具類裡的uploadIdMap要換成redis存儲