|
|
|
@ -7,23 +7,34 @@ import com.amazonaws.auth.BasicAWSCredentials;
|
|
|
|
|
import com.amazonaws.client.builder.AwsClientBuilder; |
|
|
|
|
import com.amazonaws.services.s3.AmazonS3; |
|
|
|
|
import com.amazonaws.services.s3.AmazonS3ClientBuilder; |
|
|
|
|
import com.amazonaws.services.s3.model.CompleteMultipartUploadRequest; |
|
|
|
|
import com.amazonaws.services.s3.model.DeleteObjectsRequest; |
|
|
|
|
import com.amazonaws.services.s3.model.GetObjectRequest; |
|
|
|
|
import com.amazonaws.services.s3.model.InitiateMultipartUploadRequest; |
|
|
|
|
import com.amazonaws.services.s3.model.InitiateMultipartUploadResult; |
|
|
|
|
import com.amazonaws.services.s3.model.ListObjectsRequest; |
|
|
|
|
import com.amazonaws.services.s3.model.ObjectListing; |
|
|
|
|
import com.amazonaws.services.s3.model.ObjectMetadata; |
|
|
|
|
import com.amazonaws.services.s3.model.PartETag; |
|
|
|
|
import com.amazonaws.services.s3.model.PutObjectRequest; |
|
|
|
|
import com.amazonaws.services.s3.model.S3Object; |
|
|
|
|
import com.amazonaws.services.s3.model.S3ObjectSummary; |
|
|
|
|
import com.amazonaws.services.s3.model.UploadPartRequest; |
|
|
|
|
import com.amazonaws.services.s3.model.UploadPartResult; |
|
|
|
|
import com.amazonaws.util.IOUtils; |
|
|
|
|
import com.fanruan.api.log.LogKit; |
|
|
|
|
import com.fanruan.api.util.StringKit; |
|
|
|
|
import com.fr.io.repository.FineFileEntry; |
|
|
|
|
import com.fr.io.repository.base.BaseResourceRepository; |
|
|
|
|
import com.fr.io.utils.ResourceIOUtils; |
|
|
|
|
import com.fr.log.FineLoggerFactory; |
|
|
|
|
import com.fr.stable.Filter; |
|
|
|
|
import com.fr.stable.StringUtils; |
|
|
|
|
import com.fr.third.org.apache.commons.io.output.NullOutputStream; |
|
|
|
|
import com.fr.workspace.resource.ResourceIOException; |
|
|
|
|
|
|
|
|
|
import java.io.ByteArrayInputStream; |
|
|
|
|
import java.io.IOException; |
|
|
|
|
import java.io.InputStream; |
|
|
|
|
import java.net.URL; |
|
|
|
|
import java.net.URLConnection; |
|
|
|
@ -39,6 +50,10 @@ public class S3ResourceRepository extends BaseResourceRepository {
|
|
|
|
|
|
|
|
|
|
private static final int PAGE_SIZE = 1000; |
|
|
|
|
|
|
|
|
|
private static final int PART_SIZE = 5 * 1024 * 1024; |
|
|
|
|
|
|
|
|
|
private static final int MULTIPART_UPLOAD_LIMIT = 4 * PART_SIZE; |
|
|
|
|
|
|
|
|
|
private static final String DELIMITER = "/"; |
|
|
|
|
|
|
|
|
|
public static final String HTTP = "http:"; |
|
|
|
@ -57,6 +72,8 @@ public class S3ResourceRepository extends BaseResourceRepository {
|
|
|
|
|
amazonS3ClientBuilder = amazonS3ClientBuilder.enablePathStyleAccess(); |
|
|
|
|
} |
|
|
|
|
ClientConfiguration clientConfiguration = new ClientConfiguration(); |
|
|
|
|
clientConfiguration.setMaxConnections(config.getMaxConnections()); |
|
|
|
|
LogKit.info("Max connections is {}!", clientConfiguration.getMaxConnections()); |
|
|
|
|
if (StringUtils.isNotEmpty(config.getSignerOverride())) { |
|
|
|
|
clientConfiguration.setSignerOverride(config.getSignerOverride()); |
|
|
|
|
} |
|
|
|
@ -95,15 +112,15 @@ public class S3ResourceRepository extends BaseResourceRepository {
|
|
|
|
|
ListObjectsRequest listObjectsRequest = new ListObjectsRequest().withBucketName(bucket) |
|
|
|
|
.withPrefix(dir).withDelimiter(DELIMITER); |
|
|
|
|
ObjectListing objectListing = s3.listObjects(listObjectsRequest); |
|
|
|
|
collectFileEntry(result, objectListing); |
|
|
|
|
collectFileEntry(dir, result, objectListing); |
|
|
|
|
while (objectListing.isTruncated()) { |
|
|
|
|
objectListing = s3.listNextBatchOfObjects(objectListing); |
|
|
|
|
collectFileEntry(result, objectListing); |
|
|
|
|
collectFileEntry(dir, result, objectListing); |
|
|
|
|
} |
|
|
|
|
return result.toArray(new FineFileEntry[0]); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private void collectFileEntry(List<FineFileEntry> result, ObjectListing objectListing) { |
|
|
|
|
private void collectFileEntry(String dir, List<FineFileEntry> result, ObjectListing objectListing) { |
|
|
|
|
for (S3ObjectSummary summary : objectListing.getObjectSummaries()) { |
|
|
|
|
String key = summary.getKey(); |
|
|
|
|
if (!key.endsWith(DELIMITER)) { |
|
|
|
@ -111,9 +128,11 @@ public class S3ResourceRepository extends BaseResourceRepository {
|
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
for (String prefix : objectListing.getCommonPrefixes()) { |
|
|
|
|
FineFileEntry entry = new FineFileEntry(prefix); |
|
|
|
|
entry.setDirectory(true); |
|
|
|
|
result.add(entry); |
|
|
|
|
if (StringUtils.isNotEmpty(prefix.substring(dir.length()).replaceAll(DELIMITER, StringUtils.EMPTY))) { |
|
|
|
|
FineFileEntry entry = new FineFileEntry(prefix); |
|
|
|
|
entry.setDirectory(true); |
|
|
|
|
result.add(entry); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -128,6 +147,8 @@ public class S3ResourceRepository extends BaseResourceRepository {
|
|
|
|
|
try { |
|
|
|
|
return s3.getObject(request).getObjectContent(); |
|
|
|
|
} catch (Exception e) { |
|
|
|
|
LogKit.error("[S3] Failed to read file {}", filePath); |
|
|
|
|
LogKit.error(e.getMessage(), e); |
|
|
|
|
return new ByteArrayInputStream(new byte[0]); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
@ -150,6 +171,61 @@ public class S3ResourceRepository extends BaseResourceRepository {
|
|
|
|
|
s3.putObject(bucket, path, new ByteArrayInputStream(data), metadata); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
|
public void write(String path, InputStream inputStream) throws ResourceIOException { |
|
|
|
|
long dataLength = 0; |
|
|
|
|
try { |
|
|
|
|
dataLength = inputStream.available(); |
|
|
|
|
} catch (IOException e) { |
|
|
|
|
FineLoggerFactory.getLogger().error(e.getMessage(), e); |
|
|
|
|
} |
|
|
|
|
//超过一定大小才使用分片上传,小文件来说,网络传输时间可能较短,且上传失败的风险相对较低。
|
|
|
|
|
//在网络稳定的情况下,使用分片上传可能没有太多的优势,反而增加了额外开销和复杂性
|
|
|
|
|
if (dataLength > MULTIPART_UPLOAD_LIMIT) { |
|
|
|
|
try { |
|
|
|
|
// Step 1: 初始化分片上传
|
|
|
|
|
InitiateMultipartUploadRequest initRequest = new InitiateMultipartUploadRequest(bucket, path); |
|
|
|
|
InitiateMultipartUploadResult initResponse = s3.initiateMultipartUpload(initRequest); |
|
|
|
|
String uploadId = initResponse.getUploadId(); |
|
|
|
|
|
|
|
|
|
// Step 2: 分片上传文件
|
|
|
|
|
List<PartETag> partETags = new ArrayList<>(); |
|
|
|
|
long position = 0; |
|
|
|
|
|
|
|
|
|
for (int partNumber = 1; position < dataLength; partNumber++) { |
|
|
|
|
// 最后一个分片可能小于5MB
|
|
|
|
|
long partSizeBytes = Math.min(PART_SIZE, dataLength - position); |
|
|
|
|
byte[] bytes = new byte[(int) partSizeBytes]; |
|
|
|
|
inputStream.read(bytes); |
|
|
|
|
|
|
|
|
|
// 创建上传请求
|
|
|
|
|
UploadPartRequest uploadRequest = new UploadPartRequest() |
|
|
|
|
.withBucketName(bucket) |
|
|
|
|
.withKey(path) |
|
|
|
|
.withUploadId(uploadId) |
|
|
|
|
.withPartNumber(partNumber) |
|
|
|
|
.withInputStream(new ByteArrayInputStream(bytes)) |
|
|
|
|
.withPartSize(partSizeBytes); |
|
|
|
|
|
|
|
|
|
// 上传分片
|
|
|
|
|
UploadPartResult uploadResult = s3.uploadPart(uploadRequest); |
|
|
|
|
partETags.add(uploadResult.getPartETag()); |
|
|
|
|
|
|
|
|
|
position += partSizeBytes; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Step 3: 完成分片上传
|
|
|
|
|
CompleteMultipartUploadRequest compRequest = new CompleteMultipartUploadRequest(bucket, path, uploadId, partETags); |
|
|
|
|
s3.completeMultipartUpload(compRequest); |
|
|
|
|
} catch (IOException e) { |
|
|
|
|
throw new ResourceIOException(e); |
|
|
|
|
} finally { |
|
|
|
|
ResourceIOUtils.close(inputStream); |
|
|
|
|
} |
|
|
|
|
} else { |
|
|
|
|
super.write(path, inputStream); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
|
public boolean createFile(String path) { |
|
|
|
@ -313,7 +389,11 @@ public class S3ResourceRepository extends BaseResourceRepository {
|
|
|
|
|
result.add(key.substring(key.lastIndexOf(DELIMITER) + 1)); |
|
|
|
|
} |
|
|
|
|
for (String prefix : objectListing.getCommonPrefixes()) { |
|
|
|
|
result.add(prefix.substring(prefix.lastIndexOf(DELIMITER) + 1)); |
|
|
|
|
if (StringUtils.isNotEmpty(prefix.substring(dir.length()).replaceAll(DELIMITER, StringUtils.EMPTY))) { |
|
|
|
|
String[] arr = prefix.split(DELIMITER); |
|
|
|
|
String name = arr[arr.length - 1] + DELIMITER; |
|
|
|
|
result.add(name); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -341,6 +421,8 @@ public class S3ResourceRepository extends BaseResourceRepository {
|
|
|
|
|
S3Object s3Object = s3.getObject(bucket, path); |
|
|
|
|
if (s3Object != null) { |
|
|
|
|
try { |
|
|
|
|
//s3Object要全部读完,否则会有警告
|
|
|
|
|
IOUtils.copy(s3Object.getObjectContent(), new NullOutputStream()); |
|
|
|
|
return s3Object.getObjectMetadata().getLastModified().getTime(); |
|
|
|
|
} finally { |
|
|
|
|
s3Object.close(); |
|
|
|
|