Ver código fonte

oss-sdk 模块

reghao 2 anos atrás
pai
commit
376c9d26f1

+ 96 - 0
oss-sdk/pom.xml

@@ -0,0 +1,96 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <groupId>cn.reghao.oss</groupId>
+    <artifactId>oss-sdk</artifactId>
+    <version>1.0.0-SNAPSHOT</version>
+
+    <properties>
+        <maven.compiler.source>11</maven.compiler.source>
+        <maven.compiler.target>11</maven.compiler.target>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>cn.reghao.jutil</groupId>
+            <artifactId>jdk</artifactId>
+            <version>1.0.0-SNAPSHOT</version>
+        </dependency>
+        <dependency>
+            <groupId>cn.reghao.jutil</groupId>
+            <artifactId>tool</artifactId>
+            <version>1.0.0-SNAPSHOT</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.projectlombok</groupId>
+            <artifactId>lombok</artifactId>
+            <version>1.18.6</version>
+            <optional>true</optional>
+        </dependency>
+        <dependency>
+            <groupId>ch.qos.logback</groupId>
+            <artifactId>logback-core</artifactId>
+            <version>1.2.3</version>
+        </dependency>
+        <dependency>
+            <groupId>ch.qos.logback</groupId>
+            <artifactId>logback-classic</artifactId>
+            <version>1.2.3</version>
+        </dependency>
+
+        <dependency>
+            <groupId>com.amazonaws</groupId>
+            <artifactId>aws-java-sdk-s3</artifactId>
+            <version>1.11.609</version>
+        </dependency>
+        <dependency>
+            <groupId>org.bouncycastle</groupId>
+            <artifactId>bcprov-jdk15on</artifactId>
+            <version>1.55</version>
+        </dependency>
+
+        <dependency>
+            <groupId>com.aliyun.oss</groupId>
+            <artifactId>aliyun-sdk-oss</artifactId>
+            <version>3.10.2</version>
+        </dependency>
+
+        <!--<dependency>
+            <groupId>org.apache.commons</groupId>
+            <artifactId>commons-lang3</artifactId>
+            <version>3.12.0</version>
+        </dependency>-->
+
+        <dependency>
+            <groupId>javax.xml.bind</groupId>
+            <artifactId>jaxb-api</artifactId>
+            <version>2.3.0</version>
+        </dependency>
+        <dependency>
+            <groupId>com.sun.xml.bind</groupId>
+            <artifactId>jaxb-impl</artifactId>
+            <version>2.3.0</version>
+        </dependency>
+        <dependency>
+            <groupId>com.sun.xml.bind</groupId>
+            <artifactId>jaxb-core</artifactId>
+            <version>2.3.0</version>
+        </dependency>
+        <dependency>
+            <groupId>javax.activation</groupId>
+            <artifactId>activation</artifactId>
+            <version>1.1.1</version>
+        </dependency>
+    </dependencies>
+
+    <distributionManagement>
+        <repository>
+            <id>maven-local-hosted</id>
+            <url>http://nexus.reghao.cn/repository/maven-local-hosted/</url>
+        </repository>
+    </distributionManagement>
+</project>

+ 172 - 0
oss-sdk/src/main/java/cn/reghao/oss/sdk/MultiPartBodyPublisher.java

@@ -0,0 +1,172 @@
+package cn.reghao.oss.sdk;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.UncheckedIOException;
+import java.net.http.HttpRequest;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.*;
+import java.util.function.Supplier;
+
+/**
+ * https://stackoverflow.com/questions/46392160/java-9-httpclient-send-a-multipart-form-data-request
+ *
+ * @author reghao
+ * @date 2022-11-22 16:02:02
+ */
+public class MultiPartBodyPublisher {
+    private List<PartsSpecification> partsSpecificationList = new ArrayList<>();
+    private String boundary = UUID.randomUUID().toString();
+
+    public HttpRequest.BodyPublisher build() {
+        if (partsSpecificationList.size() == 0) {
+            throw new IllegalStateException("Must have at least one part to build multipart message.");
+        }
+        addFinalBoundaryPart();
+        return HttpRequest.BodyPublishers.ofByteArrays(PartsIterator::new);
+    }
+
+    public String getBoundary() {
+        return boundary;
+    }
+
+    public MultiPartBodyPublisher addPart(String name, String value) {
+        PartsSpecification newPart = new PartsSpecification();
+        newPart.type = PartsSpecification.TYPE.STRING;
+        newPart.name = name;
+        newPart.value = value;
+        partsSpecificationList.add(newPart);
+        return this;
+    }
+
+    public MultiPartBodyPublisher addPart(String name, Path value) {
+        PartsSpecification newPart = new PartsSpecification();
+        newPart.type = PartsSpecification.TYPE.FILE;
+        newPart.name = name;
+        newPart.path = value;
+        partsSpecificationList.add(newPart);
+        return this;
+    }
+
+    public MultiPartBodyPublisher addPart(String name, Supplier<InputStream> value, String filename, String contentType) {
+        PartsSpecification newPart = new PartsSpecification();
+        newPart.type = PartsSpecification.TYPE.STREAM;
+        newPart.name = name;
+        newPart.stream = value;
+        newPart.filename = filename;
+        newPart.contentType = contentType;
+        partsSpecificationList.add(newPart);
+        return this;
+    }
+
+    private void addFinalBoundaryPart() {
+        PartsSpecification newPart = new PartsSpecification();
+        newPart.type = PartsSpecification.TYPE.FINAL_BOUNDARY;
+        newPart.value = "--" + boundary + "--";
+        partsSpecificationList.add(newPart);
+    }
+
+    static class PartsSpecification {
+
+        public enum TYPE {
+            STRING, FILE, STREAM, FINAL_BOUNDARY
+        }
+
+        TYPE type;
+        String name;
+        String value;
+        Path path;
+        Supplier<InputStream> stream;
+        String filename;
+        String contentType;
+
+    }
+
+    class PartsIterator implements Iterator<byte[]> {
+
+        private Iterator<PartsSpecification> iter;
+        private InputStream currentFileInput;
+
+        private boolean done;
+        private byte[] next;
+
+        PartsIterator() {
+            iter = partsSpecificationList.iterator();
+        }
+
+        @Override
+        public boolean hasNext() {
+            if (done) return false;
+            if (next != null) return true;
+            try {
+                next = computeNext();
+            } catch (IOException e) {
+                throw new UncheckedIOException(e);
+            }
+            if (next == null) {
+                done = true;
+                return false;
+            }
+            return true;
+        }
+
+        @Override
+        public byte[] next() {
+            if (!hasNext()) throw new NoSuchElementException();
+            byte[] res = next;
+            next = null;
+            return res;
+        }
+
+        private byte[] computeNext() throws IOException {
+            if (currentFileInput == null) {
+                if (!iter.hasNext()) return null;
+                PartsSpecification nextPart = iter.next();
+                if (PartsSpecification.TYPE.STRING.equals(nextPart.type)) {
+                    String part =
+                            "--" + boundary + "\r\n" +
+                                    "Content-Disposition: form-data; name=" + nextPart.name + "\r\n" +
+                                    "Content-Type: text/plain; charset=UTF-8\r\n\r\n" +
+                                    nextPart.value + "\r\n";
+                    return part.getBytes(StandardCharsets.UTF_8);
+                }
+                if (PartsSpecification.TYPE.FINAL_BOUNDARY.equals(nextPart.type)) {
+                    return nextPart.value.getBytes(StandardCharsets.UTF_8);
+                }
+                String filename;
+                String contentType;
+                if (PartsSpecification.TYPE.FILE.equals(nextPart.type)) {
+                    Path path = nextPart.path;
+                    filename = path.getFileName().toString();
+                    contentType = Files.probeContentType(path);
+                    if (contentType == null) contentType = "application/octet-stream";
+                    currentFileInput = Files.newInputStream(path);
+                } else {
+                    filename = nextPart.filename;
+                    contentType = nextPart.contentType;
+                    if (contentType == null) contentType = "application/octet-stream";
+                    currentFileInput = nextPart.stream.get();
+                }
+                String partHeader =
+                        "--" + boundary + "\r\n" +
+                                "Content-Disposition: form-data; name=" + nextPart.name + "; filename=" + filename + "\r\n" +
+                                "Content-Type: " + contentType + "\r\n\r\n";
+                return partHeader.getBytes(StandardCharsets.UTF_8);
+            } else {
+                byte[] buf = new byte[8192];
+                int r = currentFileInput.read(buf);
+                if (r > 0) {
+                    byte[] actualBytes = new byte[r];
+                    System.arraycopy(buf, 0, actualBytes, 0, r);
+                    return actualBytes;
+                } else {
+                    currentFileInput.close();
+                    currentFileInput = null;
+                    return "\r\n".getBytes(StandardCharsets.UTF_8);
+                }
+            }
+        }
+    }
+}

+ 129 - 0
oss-sdk/src/main/java/cn/reghao/oss/sdk/ObjectBasicService.java

@@ -0,0 +1,129 @@
+package cn.reghao.oss.sdk;
+
+import cn.reghao.jutil.jdk.result.WebResult;
+import cn.reghao.jutil.jdk.serializer.JsonConverter;
+import lombok.extern.slf4j.Slf4j;
+
+import java.io.*;
+import java.lang.reflect.Type;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.http.HttpClient;
+import java.net.http.HttpRequest;
+import java.net.http.HttpResponse;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Map;
+import java.util.function.Supplier;
+
+/**
+ * @author reghao
+ * @date 2022-11-21 17:19:04
+ */
+@Slf4j
+public class ObjectBasicService {
+    private String endpoint;
+    private String bucketName;
+    private String accessKeyId;
+    private String accessKeySecret;
+    private HttpClient httpClient = HttpClient.newBuilder().build();
+    
+    public ObjectBasicService(String endpoint, String bucketName) {
+        this.endpoint = endpoint;
+        this.bucketName = bucketName;
+        this.accessKeyId = "LTAI5t9juYR3sSP3t7fstqyt";
+        this.accessKeySecret = "Tn54Dliyk6ET3nYevum9KGRxrM8Ytt";
+    }
+
+    public void putObject(String key, File file, Map<String, String> headers)
+            throws URISyntaxException, IOException, InterruptedException {
+        String api = String.format("%s/%s", endpoint, key);
+        HttpRequest.Builder builder = HttpRequest.newBuilder(new URI(api)).version(HttpClient.Version.HTTP_1_1);
+        headers.forEach(builder::header);
+
+        HttpRequest httpRequest = builder.PUT(HttpRequest.BodyPublishers.ofFile(Path.of(file.getAbsolutePath()))).build();
+        HttpResponse<String> httpResponse = httpClient.send(httpRequest, HttpResponse.BodyHandlers.ofString());
+        int statusCode = httpResponse.statusCode();
+        String body = httpResponse.body();
+
+        /*Type type = new TypeToken<WebResult<RsaPubkey>>(){}.getType();
+        WebResult<RsaPubkey> webResult = JsonConverter.jsonToObject(body, type);
+        if (webResult.getCode() == 0) {
+            RsaPubkey rsaPubkey = webResult.getData();
+            log.info("{} - {}", rsaPubkey.getPubkey(), rsaPubkey.getR());
+        } else {
+            log.error("{}", webResult.getMsg());
+        }*/
+        System.out.println();
+    }
+
+    public void putObject(String key, InputStream inputStream, Map<String, String> headers)
+            throws URISyntaxException, IOException, InterruptedException {
+        String api = String.format("%s/%s", endpoint, key);
+        HttpRequest.Builder builder = HttpRequest.newBuilder(new URI(api)).version(HttpClient.Version.HTTP_1_1);
+        headers.forEach(builder::header);
+
+        BufferedInputStream bis = new BufferedInputStream(inputStream);
+        Supplier<? extends InputStream> streamSupplier = new Supplier<BufferedInputStream>() {
+            @Override
+            public BufferedInputStream get() {
+                return bis;
+            }
+        };
+
+        HttpRequest httpRequest = builder.PUT(HttpRequest.BodyPublishers.ofInputStream(streamSupplier)).build();
+        HttpResponse<String> httpResponse = httpClient.send(httpRequest, HttpResponse.BodyHandlers.ofString());
+    }
+
+    public void postObject(String key, File file, String contentType)
+            throws URISyntaxException, IOException, InterruptedException {
+        MultiPartBodyPublisher publisher = new MultiPartBodyPublisher()
+                .addPart("key", key)
+                .addPart("file", Paths.get(file.getAbsolutePath()))
+                .addPart("contentType", contentType);
+
+        String api = String.format("%s/", endpoint);
+        HttpRequest httpRequest = HttpRequest.newBuilder(new URI(api))
+                .version(HttpClient.Version.HTTP_1_1)
+                //.header("Host", "tnb.oss-cn-chengdu.reghao.xyz")
+                .header("Authorization", "5c34161755e3d8b44e5f165ce1b91ef9")
+                //.header("Content-Length", String.valueOf(file.length()))
+                .header("Content-Type", "multipart/form-data; boundary=" + publisher.getBoundary())
+                .POST(publisher.build())
+                .build();
+
+        HttpResponse<String> httpResponse = httpClient.send(httpRequest, HttpResponse.BodyHandlers.ofString());
+        System.out.println();
+    }
+
+    public void headObject(String objectName) throws IOException, URISyntaxException, InterruptedException {
+        String api = String.format("%s/%s", endpoint, objectName);
+        HttpRequest httpRequest = HttpRequest.newBuilder(new URI(api))
+                .version(HttpClient.Version.HTTP_1_1)
+                .method("HEAD", HttpRequest.BodyPublishers.noBody())
+                .build();
+
+        HttpResponse<String> httpResponse = httpClient.send(httpRequest, HttpResponse.BodyHandlers.ofString());
+        System.out.println();
+    }
+
+    public void getObject(String objectName) throws IOException, InterruptedException, URISyntaxException {
+        String api = String.format("%s/%s", endpoint, objectName);
+        HttpRequest httpRequest = HttpRequest.newBuilder(new URI(api))
+                .version(HttpClient.Version.HTTP_1_1)
+                .GET()
+                .build();
+
+        HttpResponse<String> httpResponse = httpClient.send(httpRequest, HttpResponse.BodyHandlers.ofString());
+        System.out.println();
+    }
+
+    public void deleteObject() {
+    }
+
+    public void deleteMultipleObjects() {
+    }
+
+    public void putObjectCopy() {
+    }
+}

+ 23 - 0
oss-sdk/src/main/java/cn/reghao/oss/sdk/ObjectMultipartUploadService.java

@@ -0,0 +1,23 @@
+package cn.reghao.oss.sdk;
+
+/**
+ * @author reghao
+ * @date 2022-12-09 10:45:42
+ */
+public class ObjectMultipartUploadService {
+    public static void initiateMultipartUpload() {
+    }
+
+    public static void uploadPart() {
+    }
+
+    public static void completeMultipartUpload() {
+    }
+
+    public static void abortMultipartUpload() {
+
+    }
+
+    public static void listParts() {
+    }
+}

+ 76 - 0
oss-sdk/src/test/java/AliOssTest.java

@@ -0,0 +1,76 @@
+import com.aliyun.oss.ClientException;
+import com.aliyun.oss.OSS;
+import com.aliyun.oss.OSSClientBuilder;
+import com.aliyun.oss.OSSException;
+import com.aliyun.oss.model.OSSObjectSummary;
+import com.aliyun.oss.model.ObjectListing;
+import com.amazonaws.auth.AWSStaticCredentialsProvider;
+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.PutObjectResult;
+
+import java.io.File;
+
+/**
+ * @author reghao
+ * @date 2022-06-20 14:31:14
+ */
+public class AliOssTest {
+    static String endpoint = "https://oss-cn-chengdu.aliyuncs.com";
+    static String accessKeyId = "LTAI5t9juYR3sSP3t7fstqyt";
+    static String accessKeySecret = "Tn54Dliyk6ET3nYevum9KGRxrM8Ytt";
+    static String bucketName = "tnb0";
+
+    static void test1() {
+        OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
+        try {
+            // ossClient.listObjects返回ObjectListing实例,包含此次listObject请求的返回结果。
+            ObjectListing objectListing = ossClient.listObjects(bucketName);
+            // objectListing.getObjectSummaries获取所有文件的描述信息。
+            for (OSSObjectSummary objectSummary : objectListing.getObjectSummaries()) {
+                System.out.println(" - " + objectSummary.getKey() + "  " +
+                        "(size = " + objectSummary.getSize() + ")");
+            }
+        } catch (OSSException oe) {
+            System.out.println("Caught an OSSException, which means your request made it to OSS, "
+                    + "but was rejected with an error response for some reason.");
+            System.out.println("Error Message:" + oe.getErrorMessage());
+            System.out.println("Error Code:" + oe.getErrorCode());
+            System.out.println("Request ID:" + oe.getRequestId());
+            System.out.println("Host ID:" + oe.getHostId());
+        } catch (ClientException ce) {
+            System.out.println("Caught an ClientException, which means the client encountered "
+                    + "a serious internal problem while trying to communicate with OSS, "
+                    + "such as not being able to access the network.");
+            System.out.println("Error Message:" + ce.getMessage());
+        } finally {
+            if (ossClient != null) {
+                ossClient.shutdown();
+            }
+        }
+    }
+
+    static void test() {
+        AmazonS3 s3Client = AmazonS3ClientBuilder.standard()
+                .withCredentials(new AWSStaticCredentialsProvider(new BasicAWSCredentials(accessKeyId, accessKeySecret)))
+                .withEndpointConfiguration(new AwsClientBuilder.EndpointConfiguration(
+                        "https://oss-cn-chengdu.aliyuncs.com",
+                        ""))
+                .withPathStyleAccessEnabled(false)
+                .withChunkedEncodingDisabled(true)
+                .build();
+
+        String filePath = "/home/reghao/data/video/mp4/snow_vertical.mp4";
+        File file = new File(filePath);
+        String key = file.getName();
+        PutObjectResult putObjectResult = s3Client.putObject(bucketName, key, file);
+
+        System.out.println();
+    }
+
+    public static void main(String[] args) {
+        test();
+    }
+}

+ 58 - 0
oss-sdk/src/test/java/BucketTest.java

@@ -0,0 +1,58 @@
+import com.amazonaws.auth.AWSStaticCredentialsProvider;
+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.Bucket;
+import com.amazonaws.services.s3.model.CannedAccessControlList;
+import com.amazonaws.services.s3.model.CreateBucketRequest;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.RandomAccessFile;
+
+/**
+ * @author reghao
+ * @date 2023-03-21 10:30:30
+ */
+public class BucketTest {
+    static String region = "chengdu";
+    static String endpoint = "http://oss-cn-chengdu.reghao.cn/";
+    static String accessKey = "accesskey123456";
+    static String secretKey = "secretKey123456";
+    static AmazonS3 s3Client;
+    static {
+        s3Client = AmazonS3ClientBuilder.standard()
+                .withCredentials(new AWSStaticCredentialsProvider(new BasicAWSCredentials(accessKey, secretKey)))
+                .withEndpointConfiguration(new AwsClientBuilder.EndpointConfiguration(endpoint, region))
+                .withPathStyleAccessEnabled(false)
+                .withChunkedEncodingDisabled(true)
+                .build();
+    }
+
+    static void createBucket(String bucketName) {
+        CreateBucketRequest request = new CreateBucketRequest(bucketName);
+        //设置桶的权限,如果不设置,默认为Private
+        request.setCannedAcl(CannedAccessControlList.PublicRead);
+        s3Client.createBucket(request);
+    }
+
+    static void deleteBucket(String bucketName) {
+        s3Client.deleteBucket(bucketName);
+    }
+
+    static void listBucket() {
+        for (Bucket bucket : s3Client.listBuckets()) {
+            System.out.println(" - " + bucket.getName());
+        }
+    }
+
+    static boolean bucketExist(String bucketName) {
+        return s3Client.doesBucketExistV2(bucketName);
+    }
+
+    public static void main(String[] args) {
+
+    }
+}

+ 159 - 0
oss-sdk/src/test/java/ObjectTest.java

@@ -0,0 +1,159 @@
+import cn.reghao.jutil.jdk.converter.ByteHex;
+import cn.reghao.jutil.jdk.io.FilePart;
+import cn.reghao.jutil.jdk.security.Base64Util;
+import cn.reghao.jutil.jdk.security.DigestUtil;
+import com.amazonaws.auth.AWSStaticCredentialsProvider;
+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.*;
+import lombok.extern.slf4j.Slf4j;
+
+import java.io.*;
+import java.util.*;
+
+/**
+ * @author reghao
+ * @date 2022-06-20 14:31:14
+ */
+@Slf4j
+public class ObjectTest {
+    static String region = "chengdu";
+    static String endpoint = "http://oss-cn-chengdu.reghao.cn/";
+    //static String endpoint = "http://localhost:8010/";
+    static String bucketName = "tnb";
+    static String accessKey = "accesskey123456";
+    static String secretKey = "secretKey123456";
+    static AmazonS3 s3Client;
+    static {
+        s3Client = AmazonS3ClientBuilder.standard()
+                .withCredentials(new AWSStaticCredentialsProvider(new BasicAWSCredentials(accessKey, secretKey)))
+                .withEndpointConfiguration(new AwsClientBuilder.EndpointConfiguration(endpoint, region))
+                .withPathStyleAccessEnabled(false)
+                .withChunkedEncodingDisabled(true)
+                .build();
+    }
+    static FilePart filePart = new FilePart();
+
+    static void put(String key, File file) {
+        PutObjectResult putObjectResult = s3Client.putObject(bucketName, key, file);
+        String eTag = putObjectResult.getETag();
+        byte[] bytes = ByteHex.hex2Bytes(eTag);
+        try {
+            String md5Base64 = Base64Util.encode(DigestUtil.md5sum(bytes));
+            System.out.println();
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        System.out.println();
+    }
+
+    static boolean objectExist(String bucketName, String objectName) {
+        return s3Client.doesObjectExist(bucketName, objectName);
+    }
+
+    static void get() {
+        String key = "";
+        S3Object s3Object = s3Client.getObject(bucketName, key);
+        ObjectMetadata objectMetadata = s3Client.getObjectMetadata(bucketName, key);
+    }
+
+    static void upload(String key, File file) {
+        ObjectMetadata objectMetadata = new ObjectMetadata();
+        objectMetadata.setContentType("application/xml");
+        InitiateMultipartUploadRequest initRequest = new InitiateMultipartUploadRequest(bucketName, key);
+
+        InitiateMultipartUploadResult initResult = s3Client.initiateMultipartUpload(initRequest);
+        String uploadId = initResult.getUploadId();
+
+        // 分段大小在 5MB - 5GB 之间,只有最后一个分段才允许小于 5MB,不可避免的
+        int minPartSize = filePart.getPartSize();
+        long len = file.length();
+        long total = len/minPartSize;
+        if (len % minPartSize != 0) {
+            total += 1;
+        }
+
+        List<PartETag> partETags = new ArrayList<>();
+        try {
+            for (int i = 0; i < total; i++) {
+                int start = i*minPartSize;
+                byte[] part = filePart.getPart(file.getAbsolutePath(), minPartSize, start);
+
+                UploadPartRequest uploadRequest = new UploadPartRequest()
+                        .withBucketName(bucketName)
+                        .withKey(key)
+                        .withUploadId(initResult.getUploadId())
+                        .withPartNumber(i)
+                        .withInputStream(new ByteArrayInputStream(part))
+                        .withPartSize(part.length);
+
+                // 第二步,上传分段,并把当前段的 PartETag 放到列表中
+                UploadPartResult uploadPartResult = s3Client.uploadPart(uploadRequest);
+                partETags.add(uploadPartResult.getPartETag());
+                log.info("第 {} 个分片上传完成", i);
+
+                if (i == 15) {
+                    //throw new IOException("模拟异常");
+                }
+            }
+
+            // 检查分块是否全部上传,分块MD5是否与本地计算相符,如果不符或者缺少可以重新上传
+            List<PartETag> partETags1 = new ArrayList<>();
+            int nextMarker = 0;
+            while (true) {
+                ListPartsRequest listPartsRequest = new ListPartsRequest(bucketName, key, uploadId);
+                listPartsRequest.setPartNumberMarker(nextMarker);
+                PartListing partList = s3Client.listParts(listPartsRequest);
+
+                for (PartSummary ps : partList.getParts()) {
+                    nextMarker++;
+                    partETags1.add(new PartETag(ps.getPartNumber(), ps.getETag()));
+                }
+
+                if (!partList.isTruncated()) {
+                    break;
+                }
+            }
+
+            /*CopyPartRequest copyPartRequest = new CopyPartRequest();
+            copyPartRequest.setUploadId(uploadId);
+            copyPartRequest.setPartNumber(1);
+            copyPartRequest.setSourceKey("");
+            copyPartRequest.setFirstByte(0L);
+            copyPartRequest.setLastByte(10L);
+            CopyPartResult copyPartResult  = s3Client.copyPart(copyPartRequest);*/
+
+            // 第三步,完成上传
+            CompleteMultipartUploadRequest compRequest =
+                    new CompleteMultipartUploadRequest(bucketName, key, initResult.getUploadId(), partETags);
+            CompleteMultipartUploadResult completeMultipartUploadResult = s3Client.completeMultipartUpload(compRequest);
+            System.out.println();
+        } catch (Exception e) {
+            AbortMultipartUploadRequest request = new AbortMultipartUploadRequest(bucketName, key, initResult.getUploadId());
+            s3Client.abortMultipartUpload(request);
+            System.out.println("Failed to upload, " + e.getMessage());
+        }
+    }
+
+    public static void main(String[] args) throws Exception {
+        String filePath = "/home/reghao/Downloads/sxd.mp4";
+        File file = new File(filePath);
+        String key = String.format("a/%s", file.getName());
+        //put(key, file);
+        upload(key, file);
+
+        /*FileInputStream fis = new FileInputStream(filePath);
+        String sha256Hex = DigestUtil.sha256sum(fis);
+        */
+
+        /*String srcFile = "/home/reghao/Downloads/1/sanxingdui.mp4";
+        String destDir = "/home/reghao/Downloads/1/parts/";
+        filePart.split(srcFile, destDir);
+
+        String srcDir = "/home/reghao/Downloads/1/parts";
+        String destFile = "/home/reghao/Downloads/1/merged.dat";
+        filePart.merge(srcDir, destFile);*/
+    }
+}

+ 30 - 0
oss-sdk/src/test/java/RegionTest.java

@@ -0,0 +1,30 @@
+import com.amazonaws.auth.AWSStaticCredentialsProvider;
+import com.amazonaws.auth.BasicAWSCredentials;
+import com.amazonaws.client.builder.AwsClientBuilder;
+import com.amazonaws.services.s3.AmazonS3;
+import com.amazonaws.services.s3.AmazonS3ClientBuilder;
+
+/**
+ * @author reghao
+ * @date 2023-01-16 10:41:34
+ */
+public class RegionTest {
+    static String region = "chengdu";
+    static String endpoint = "http://oss-cn-chengdu.reghao.cn/";
+    static String bucketName = "tnb";
+    static String accessKey = "accesskey123456";
+    static String secretKey = "secretKey123456";
+    static AmazonS3 s3Client;
+    static {
+        s3Client = AmazonS3ClientBuilder.standard()
+                .withCredentials(new AWSStaticCredentialsProvider(new BasicAWSCredentials(accessKey, secretKey)))
+                .withEndpointConfiguration(new AwsClientBuilder.EndpointConfiguration(endpoint, region))
+                .withPathStyleAccessEnabled(false)
+                .withChunkedEncodingDisabled(true)
+                .build();
+    }
+
+    static void test() {
+        String regionName = s3Client.getRegionName();
+    }
+}

+ 143 - 0
oss-sdk/src/test/java/S3EncTest.java

@@ -0,0 +1,143 @@
+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.services.s3.AmazonS3Encryption;
+import com.amazonaws.services.s3.AmazonS3EncryptionClientBuilder;
+import com.amazonaws.services.s3.model.*;
+
+import javax.crypto.KeyGenerator;
+import javax.crypto.SecretKey;
+import javax.crypto.spec.SecretKeySpec;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.security.*;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.PKCS8EncodedKeySpec;
+import java.security.spec.X509EncodedKeySpec;
+
+/**
+ * @author reghao
+ * @date 2023-03-21 11:51:45
+ */
+public class S3EncTest {
+    static String region = "chengdu";
+    static String endpoint = "http://oss-cn-chengdu.reghao.cn/";
+    static String bucketName = "tnb";
+    static String accessKey = "your-accesskey";
+    static String secretKey = "your-secretKey ";
+    static AmazonS3Encryption s3Client;
+
+    static void init() throws IOException, NoSuchAlgorithmException, InvalidKeySpecException {
+        //1.获取CMK,客户端主密钥,可以使用对称和分对称两种方式,下述使用的是非对称的
+        String keyDir = "/home/reghao/Downloads/key";
+        KeyPair keyPair = loadRsaKeyPair(keyDir, "RSA");
+
+        // 2. Construct an instance of AmazonS3Encryption.
+        EncryptionMaterials encryptionMaterials = new EncryptionMaterials(keyPair);
+        ClientConfiguration configuration = new ClientConfiguration();
+        configuration.setProtocol(Protocol.HTTPS);
+        CryptoConfiguration cryptoConfiguration = new CryptoConfiguration();
+        //支持EncryptionOnly,AuthenticatedEncryption,StrictAuthenticatedEncryption,默认是EncryptionOnly,StrictAuthenticatedEncryption不支持range请求
+        cryptoConfiguration.setCryptoMode(CryptoMode.StrictAuthenticatedEncryption);
+        //保存加密信息的方式,有两种方式,Instruction模式和Metadata模式,由于NOS分块上传和S3支持上存在一些差异,导致metadata保存的方式大文件下载时由于找不到加密信息而不解密
+        cryptoConfiguration.setStorageMode(CryptoStorageMode.InstructionFile);
+        EncryptionMaterialsProvider encryptionMaterialsProvider = new StaticEncryptionMaterialsProvider(encryptionMaterials);
+
+        ClientConfiguration clientConfiguration = new ClientConfiguration();
+        clientConfiguration.setProtocol(Protocol.HTTPS);
+        AwsClientBuilder.EndpointConfiguration endpointConfiguration = new AwsClientBuilder.EndpointConfiguration(endpoint, region);
+        s3Client = AmazonS3EncryptionClientBuilder.standard()
+                .withCryptoConfiguration(cryptoConfiguration)
+                .withCredentials(new AWSStaticCredentialsProvider(new BasicAWSCredentials(accessKey,secretKey)))
+                .withEncryptionMaterials(encryptionMaterialsProvider)
+                .withClientConfiguration(clientConfiguration)
+                .withEndpointConfiguration(endpointConfiguration)
+                .build();
+    }
+
+    //loadKeyPair的实现方式,非对称加密algorithm = RSA
+    static KeyPair loadRsaKeyPair(String path, String algorithm)
+            throws IOException, NoSuchAlgorithmException, InvalidKeySpecException {
+        // read public key from file
+        File filePublicKey = new File(path + "/public.key");
+        FileInputStream fis = new FileInputStream(filePublicKey);
+        byte[] encodedPublicKey = new byte[(int) filePublicKey.length()];
+        fis.read(encodedPublicKey);
+        fis.close();
+
+        // read private key from file
+        File filePrivateKey = new File(path + "/private.key");
+        fis = new FileInputStream(filePrivateKey);
+        byte[] encodedPrivateKey = new byte[(int) filePrivateKey.length()];
+        fis.read(encodedPrivateKey);
+        fis.close();
+
+        // Convert them into KeyPair
+        KeyFactory keyFactory = KeyFactory.getInstance(algorithm);
+        X509EncodedKeySpec publicKeySpec = new X509EncodedKeySpec(
+                encodedPublicKey);
+        PublicKey publicKey = keyFactory.generatePublic(publicKeySpec);
+
+        PKCS8EncodedKeySpec privateKeySpec = new PKCS8EncodedKeySpec(
+                encodedPrivateKey);
+        PrivateKey privateKey = keyFactory.generatePrivate(privateKeySpec);
+
+        return new KeyPair(publicKey, privateKey);
+    }
+
+    //对称加密,algorithm = AES
+    static SecretKey loadAesKeyPair(String path, String algorithm) throws IOException {
+        //Read private key from file.
+        File keyFile = new File(path);
+        FileInputStream keyfis = new FileInputStream(keyFile);
+        byte[] encodedPrivateKey = new byte[(int) keyFile.length()];
+        keyfis.read(encodedPrivateKey);
+        keyfis.close();
+        //Generate secret key.
+        return new SecretKeySpec(encodedPrivateKey, algorithm);
+    }
+
+    //生成Key的方式,非对称加密
+    static KeyPair genKeyPair(String algorithm, int bitLength) throws NoSuchAlgorithmException {
+        KeyPairGenerator keyGenerator = KeyPairGenerator.getInstance(algorithm);
+        SecureRandom srand = new SecureRandom();
+        keyGenerator.initialize(bitLength, srand);
+        return keyGenerator.generateKeyPair();
+    }
+
+    //生成Key的方式,对称加密
+    static SecretKey generateCMasterKey() throws NoSuchAlgorithmException {
+        KeyGenerator symKeyGenerator = KeyGenerator.getInstance("AES");
+        symKeyGenerator.init(256);
+        return  symKeyGenerator.generateKey();
+    }
+
+    //保存Key,该key只要生成一次就好了,要妥善保管,如果该key丢失了,那么意味着通过该key加密的数据将没法解密
+    //非对称
+    static void saveKeyPair(String dir, KeyPair keyPair) throws IOException {
+        PrivateKey privateKey = keyPair.getPrivate();
+        PublicKey publicKey = keyPair.getPublic();
+
+        X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(publicKey.getEncoded());
+        FileOutputStream fos = new FileOutputStream(dir + "/public.key");
+        fos.write(x509EncodedKeySpec.getEncoded());
+        fos.close();
+
+        PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(privateKey.getEncoded());
+        fos = new FileOutputStream(dir + "/private.key");
+        fos.write(pkcs8EncodedKeySpec.getEncoded());
+        fos.close();
+    }
+
+    //对称
+    static void saveSymmetricKey(String path, SecretKey secretKey) throws IOException {
+        X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(secretKey.getEncoded());
+        FileOutputStream keyfos = new FileOutputStream(path);
+        keyfos.write(x509EncodedKeySpec.getEncoded());
+        keyfos.close();
+    }
+}