package com.yizhi.application.course.util;

import com.alibaba.fastjson.JSON;
import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClient;
import com.aliyun.oss.model.*;
import org.springframework.util.StringUtils;

import java.io.*;
import java.net.URL;
import java.nio.file.Paths;
import java.util.Date;
import java.util.List;

public class UploadOSSUtils {

    // 默认配置
    // Endpoint以杭州为例，其它Region请按实际情况填写。
    public static final String ENDPOINT = "http://oss-cn-hangzhou.aliyuncs.com";
    public static final String ACCESSKEYID = "LTAInZlfTwk5fCDJ";
    public static final String ACCESSKEYSECRET = "UavtbFb2vUvRxiwUMLvUEelXTQtHCB";
    public static final String BUCKETNAME = "fulan-test";
    // 生产配置
    public static final String ENDPOINT_PROD = "http://oss-cn-shanghai.aliyuncs.com";
    // 阿里云主账号AccessKey拥有所有API的访问权限，风险很高。强烈建议您创建并使用RAM账号进行API访问或日常运维，请登录 https://ram.console.aliyun.com 创建RAM账号。
    public static final String ACCESSKEYID_PROD = "LTAIeBliOjnD6v25";
    public static final String ACCESSKEYSECRET_PROD = "qd6iAV7tUvumvEiXxv9IGJbSdHcnmH";
    public static final String BUCKETNAME_PROD = "cloud-wmy";

    public static final String checkpointFile = ".ucp";

    // 创建存储空间
    public static boolean createBucket(String bucketName, String active) {

        // 创建OSSClient实例。
        //OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);// 新版本使用
        //OSS ossClient = new OSSClient(endpoint, accessKeyId, accessKeySecret); // 旧的方法
        OSS ossClient = generateOssClient(active);
        // 创建存储空间。
        //String bucketName = "<yourBucketName>";
        boolean exists = ossClient.doesBucketExist(bucketName);//判断存储空间是否存在
        if (exists) {
            return false;
        }
        // 您可以在创建存储空间时指定存储空间的权限和存储类型。
        CreateBucketRequest createBucketRequest = new CreateBucketRequest(bucketName);
        // 设置存储空间的权限为公共读，默认是私有。
        createBucketRequest.setCannedACL(CannedAccessControlList.PublicRead);//Private私有；PublicRead 公共读；PublicReadWrite 公共读写
        // 设置存储空间的存储类型为低频访问类型，默认是标准类型。
        createBucketRequest.setStorageClass(StorageClass.IA);
        // 新建存储空间默认为标准存储类型，私有权限。
        ossClient.createBucket(bucketName);

        //获取存储空间的信息
        // 存储空间的信息包括地域（Region或Location）、创建日期（CreationDate）、拥有者（Owner）、权限（Grants）等。
        BucketInfo info = ossClient.getBucketInfo("<yourBucketName>");
        // 获取地域。
        info.getBucket().getLocation();
        String location = ossClient.getBucketLocation("<yourBucketName>");// 或者这样。获取存储空间的地域
        // 获取创建日期。
        info.getBucket().getCreationDate();
        // 获取拥有者信息。
        info.getBucket().getOwner();
        // 获取权限信息。
        info.getGrants();
        AccessControlList acl = ossClient.getBucketAcl("<yourBucketName>");// 获取存储空间的访问权限。

        ListBucketsRequest listBucketsRequest = new ListBucketsRequest();
        // 列举指定前缀的存储空间。
        listBucketsRequest.setPrefix("<yourBucketPrefix>");
        // 列举指定marker之后的存储空间。
        //listBucketsRequest.setMarker("<yourBucketMarker>");
        // 限定此次列举存储空间的个数为500。默认值为100，最大值为1000。
        //listBucketsRequest.setMaxKeys(500);
        BucketList bucketList = ossClient.listBuckets(listBucketsRequest);
        for (Bucket bucket : bucketList.getBucketList()) {
            System.out.println(" - " + bucket.getName());
        }

        // 删除存储空间。
        /*ossClient.deleteBucket("<yourBucketName>");*/
        // 清空生命周期规则
        //ossClient.deleteBucketLifecycle(bucketName);
        // 查看生命周期规则
        List<LifecycleRule> rules = ossClient.getBucketLifecycle(bucketName);
        for (LifecycleRule rule : rules) {
            System.out.println(rule.getId());
            System.out.println(rule.getPrefix());
            System.out.println(rule.getExpirationDays());
            System.out.println(rule.getCreatedBeforeDate());
            if (rule.hasAbortMultipartUpload()) {
                System.out.println(rule.getAbortMultipartUpload().getExpirationDays());
                System.out.println(rule.getAbortMultipartUpload().getCreatedBeforeDate());
            }
        }

        // 关闭OSSClient。
        ossClient.shutdown();

        //设置生命周期规则
        SetBucketLifecycleRequest request = new SetBucketLifecycleRequest(bucketName);

        // 设置规则ID和文件前缀。
        String ruleId0 = "rule0";
        String matchPrefix0 = "A0/";
        String ruleId1 = "rule1";
        String matchPrefix1 = "A1/";
        String ruleId2 = "rule2";
        String matchPrefix2 = "A2/";
        String ruleId3 = "rule3";
        String matchPrefix3 = "A3/";

        // 距最后修改时间3天后过期。
        request.AddLifecycleRule(new LifecycleRule(ruleId0, matchPrefix0, LifecycleRule.RuleStatus.Enabled, 3));

        // 指定日期之前创建的文件过期。
        LifecycleRule rule = new LifecycleRule(ruleId1, matchPrefix1, LifecycleRule.RuleStatus.Enabled);
        //rule.setCreatedBeforeDate(DateUtil.parseISO8601Date("2022-10-12T00:00:00.000Z"));
        request.AddLifecycleRule(rule);

        // 分片3天后过期。
        //rule = new LifecycleRule(ruleId2, matchPrefix2, RuleStatus.Enabled);
        LifecycleRule.AbortMultipartUpload abortMultipartUpload = new LifecycleRule.AbortMultipartUpload();
        abortMultipartUpload.setExpirationDays(3);
        rule.setAbortMultipartUpload(abortMultipartUpload);
        request.AddLifecycleRule(rule);

        // 指定日期之前的分片过期。
        //rule = new LifecycleRule(ruleId3, matchPrefix3, RuleStatus.Enabled);
        abortMultipartUpload = new LifecycleRule.AbortMultipartUpload();
        //abortMultipartUpload.setCreatedBeforeDate(DateUtil.parseISO8601Date("2022-10-12T00:00:00.000Z"));
        rule.setAbortMultipartUpload(abortMultipartUpload);
        request.AddLifecycleRule(rule);

        ossClient.setBucketLifecycle(request);

        return true;
    }

    /**
     * 简单上传
     * @param uploadFile
     * @param objectName
     * @param active
     * @return
     */
    public static String simpleUpload(String uploadFile, String objectName, String active) {
        String netFilePath;
        // 创建OSSClient实例。
        OSS ossClient = generateOssClient(active);
        String bucketName = getBucketName(active);

        // 判断文件是否存在。doesObjectExist还有一个参数isOnlyInOSS，如果为true则忽略302重定向或镜像；如果为false，则考虑302重定向或镜像。
        //boolean found = ossClient.doesObjectExist(bucketName, objectName);
        // 删除文件。
        //ossClient.deleteObject(bucketName, objectName);
        // 批量删除 List<String> keys
        //DeleteObjectsResult deleteObjectsResult = ossClient.deleteObjects(new DeleteObjectsRequest(bucketName).withKeys(keys));
        //List<String> deletedObjects = deleteObjectsResult.getDeletedObjects();

        // 上传字符串。
        /*String content = "Hello OSS";
        ossClient.putObject("<yourBucketName>", "<yourObjectName>", new ByteArrayInputStream(content.getBytes()));*/
        // 上传文件。<yourLocalFile>由本地文件路径加文件名包括后缀组成，例如/users/local/myfile.txt。
        PutObjectResult result = ossClient.putObject(bucketName, objectName, new File(uploadFile));
        System.out.println(active+"简单上传："+uploadFile+"其中objectName="+objectName+"，上传结果="+JSON.toJSONString(result));
        //URL url = ossClient.generatePresignedUrl(bucketName,objectName);
        //netFilePath = result.getResponse().getUri();
        netFilePath = "http://fulan-test.oss-cn-hangzhou.aliyuncs.com/"+objectName;

        // 生成临时授权的URL，第三方可以用这个URL进行下载操作
        //Date expiration = new Date(System.currentTimeMillis() + 3600L * 1000 * 24 * 180);//设置该URL的有效期180天
        //URL url = ossClient.generatePresignedUrl(bucketName, objectName, expiration);// 生成URL,第二参数可以添加全局的private static String folder = "";//目录，访问就是+拼接
        //netFilePath = url.getPath();
        // 关闭OSSClient。
        ossClient.shutdown();

        return netFilePath;

    }

    public static String resumingUpload(String uploadFile, String objectName, String active) throws Throwable {
        String netFilePath;
        // 创建OSSClient实例。
        OSS ossClient = generateOssClient(active);
        String bucketName = getBucketName(active);

        ObjectMetadata meta = new ObjectMetadata();
        // 指定上传的内容类型。
        //meta.setContentType("text/plain");
        //meta.setContentType(getContentType(uploadFile));

        // 通过UploadFileRequest设置多个参数。
        UploadFileRequest uploadFileRequest = new UploadFileRequest(bucketName, objectName);

        // 通过UploadFileRequest设置单个参数。
        //uploadFileRequest.setUploadFile("<yourLocalFile>");
        uploadFileRequest.setUploadFile(uploadFile);
        // 指定上传并发线程数，默认为1。
        uploadFileRequest.setTaskNum(5);
        // 指定上传的分片大小，范围为100KB~5GB，默认为文件大小/10000。
        uploadFileRequest.setPartSize(1 * 1024 * 1024);
        // 开启断点续传，默认关闭。
        uploadFileRequest.setEnableCheckpoint(true);
        // 记录本地分片上传结果的文件。开启断点续传功能时需要设置此参数，上传过程中的进度信息会保存在该文件中，
        // 如果某一分片上传失败，再次上传时会根据文件中记录的点继续上传。上传完成后，该文件会被删除。默认与待上传的本地文件同目录，为uploadFile.ucp。
        //uploadFileRequest.setCheckpointFile("<yourCheckpointFile>");
        if (StringUtils.isEmpty(checkpointFile))
        {
            uploadFileRequest.setCheckpointFile("uploadFile.ucp");
        }else {
            uploadFileRequest.setCheckpointFile(Paths.get(uploadFile).getFileName()+checkpointFile);
        }
        // 文件的元数据。
        uploadFileRequest.setObjectMetadata(meta);
        // 设置上传成功回调，参数为Callback类型。
        //uploadFileRequest.setCallback("<yourCallbackEvent>");

        // 断点续传上传。
        UploadFileResult result = ossClient.uploadFile(uploadFileRequest);
        System.out.println(active+"断点续传："+uploadFile+"其中objectName="+objectName+"，上传结果="+JSON.toJSONString(result));
        netFilePath = result.getMultipartUploadResult().getLocation();
        // 关闭OSSClient。
        ossClient.shutdown();

        return netFilePath;
    }

    /**
     * 简单下载
     * @param downloadLocalFile
     * @param objectName
     * @param active
     * @return
     */
    public static boolean simpledDwnload(String downloadLocalFile, String objectName, String active) throws IOException {
        OSS ossClient = generateOssClient(active);
        String bucketName = getBucketName(active);

        // 下载到流文件
        // ossObject包含文件所在的存储空间名称、文件名称、文件元信息以及一个输入流。
        //OSSObject ossObject = ossClient.getObject(bucketName, objectName);
        // 读取文件内容。
        /*System.out.println("Object content:");
        BufferedReader reader = new BufferedReader(new InputStreamReader(ossObject.getObjectContent()));
        while (true) {
            String line = reader.readLine();
            if (line == null) break;

            System.out.println("\n" + line);
        }*/
        // 数据读取完成后，获取的流必须关闭，否则会造成连接泄漏，导致请求无连接可用，程序无法正常工作。
        //reader.close();

        //下载到本地文件
        // 下载OSS文件到本地文件。如果指定的本地文件存在会覆盖，不存在则新建。
        ossClient.getObject(new GetObjectRequest(bucketName, objectName), new File(downloadLocalFile));

        // 限定下载
        //GetObjectRequest request = new GetObjectRequest(bucketName, objectName);
        // 设置限定条件。
        //request.setModifiedSinceConstraint(new Date());
        // 下载OSS文件到本地文件。
        // If-Modified-Since 如果指定的时间早于实际修改时间，则正常传输文件，否则返回错误（304 Not modified）。
        // If-Unmodified-Since 如果指定的时间等于或者晚于文件实际修改时间，则正常传输文件，否则返回错误（412 Precondition failed）。
        // If-Match 如果指定的ETag和OSS文件的ETag匹配，则正常传输文件，否则返回错误（412 Precondition failed）
        // If-None-Match 如果指定的ETag和OSS文件的ETag不匹配，则正常传输文件，否则返回错误（304 Not modified）。
        //ossClient.getObject(request, new File("<yourLocalFile>"));

        // 带进度条的上传下载（下载进度条的完整代码请参见GitHub。）
        // https://help.aliyun.com/document_detail/84829.html?spm=a2c4g.11186623.6.847.61417286FK9MeU
        // 关闭OSSClient。
        ossClient.shutdown();

        return true;
    }

    public static boolean resumingDwnload(String downloadLocalFile, String objectName, String active) throws Throwable {
        OSS ossClient = generateOssClient(active);
        String bucketName = getBucketName(active);
        // 下载请求，10个任务并发下载，启动断点续传。
        DownloadFileRequest downloadFileRequest = new DownloadFileRequest(bucketName, objectName);
        downloadFileRequest.setDownloadFile(downloadLocalFile);
        downloadFileRequest.setPartSize(1 * 1024 * 1024);
        downloadFileRequest.setTaskNum(10);
        downloadFileRequest.setEnableCheckpoint(true);
        //downloadFileRequest.setCheckpointFile("<yourCheckpointFile>");
        if (StringUtils.isEmpty(checkpointFile))
        {
            downloadFileRequest.setCheckpointFile("downloadFile.ucp");
        }else {
            downloadFileRequest.setCheckpointFile(objectName+checkpointFile);
        }
        // 下载文件。
        DownloadFileResult downloadRes = ossClient.downloadFile(downloadFileRequest);
        // 下载成功时，会返回文件元信息。
        downloadRes.getObjectMetadata();

        // 关闭OSSClient。
        ossClient.shutdown();

        return true;

    }

    /**
     * 创建oss客服端
     *
     * @param active
     * @return
     */
    private static OSS generateOssClient(String active) {
        OSS ossClient;
        if ("prod".equals(active) || "uat".equals(active)) {//"dev".equals(active) ||
            ossClient = new OSSClient(ENDPOINT_PROD, ACCESSKEYID_PROD, ACCESSKEYSECRET_PROD);
        } else {
            ossClient = new OSSClient(ENDPOINT, ACCESSKEYID, ACCESSKEYSECRET);
        }
        return ossClient;
    }

    private static String getBucketName(String active) {
        String bucketName;
        if ("prod".equals(active) || "uat".equals(active)) {//"dev".equals(active) ||
            bucketName = BUCKETNAME_PROD;
        } else {
            bucketName = BUCKETNAME;
        }
        return bucketName;
    }

    /**oss获取文件下载路径； 第三方可以用这个URL进行下载上传操作
          * @param keyname
          * @return
          */
/*    public URL getUrl(String keyname){
        OSSClient client = new OSSClient(endpoint, accessKeyId, accessKeySecret);
        // 设置URL过期时间为1小时
        Date expiration = new Date(new Date().getTime() + 3600 * 10000);
        // 生成URL
        URL url = client.generatePresignedUrl(bucketName, firstKey+keyname, expiration);
        return url;
    }*/


    /**
     * 通过文件名判断并获取OSS服务文件上传时文件的contentType
     *
     * @param fileName 文件名
     * @return 文件的contentType
     */
    public static final String getContentType(String fileName) {
        String fileExtension = fileName.substring(fileName.lastIndexOf("."));
        if (".bmp".equalsIgnoreCase(fileExtension))
            return "image/bmp";
        if (".gif".equalsIgnoreCase(fileExtension))
            return "image/gif";
        if (".jpeg".equalsIgnoreCase(fileExtension))
            return "image/jpeg";
        if (".jpg".equalsIgnoreCase(fileExtension))
            return "image/jpg";
        if (".png".equalsIgnoreCase(fileExtension))
            return "image/png";
        if (".html".equalsIgnoreCase(fileExtension))
            return "text/html";
        if (".vsd".equalsIgnoreCase(fileExtension))
            return "application/vnd.visio";
        if (".ppt".equalsIgnoreCase(fileExtension) || "pptx".equalsIgnoreCase(fileExtension))
            return "application/vnd.ms-powerpoint";
        if (".doc".equalsIgnoreCase(fileExtension) || "docx".equalsIgnoreCase(fileExtension))
            return "application/msword";
        if (".xml".equalsIgnoreCase(fileExtension))
            return "text/xml";
        return "text/plain";
    }
}
