package cn.com.ty.lift.common.aliservice.aliyunoss;

import cn.hutool.core.collection.IterUtil;
import cn.hutool.core.util.StrUtil;
import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClientBuilder;
import com.aliyun.oss.model.*;
import lombok.extern.slf4j.Slf4j;

import java.io.*;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;

/**
 * <p>
 *     aliyun OSS工具类
 * </p>
 * @author wcz
 * @since  2019/11/29
 */
@Slf4j
public class AliyunOSS {

    // Endpoint以杭州为例，其它Region请按实际情况填写。
    private String endpoint        = "http://oss-cn-beijing.aliyuncs.com";
    // 阿里云主账号AccessKey拥有所有API的访问权限，风险很高。强烈建议您创建并使用RAM账号进行API访问或日常运维，请登录 https://ram.console.aliyun.com 创建RAM账号。
    private String accessKeyId     = "LTAI4FkSqFZa6LH9HqAsVott";
    private String accessKeySecret = "w7GXuh5tf1hduQuZ2AzT3a4q14BI1i";
    //存储空间名
    private String bucketName      = "temp15827479607";

    //OSSClient实例
    private OSS ossClient;

    private static class Builder{
        private static OSSClientBuilder ossClientBuilder = new OSSClientBuilder();
    }
    /**
     * 无参构造，初始化oss
     */
    private AliyunOSS() {
        init("Default");
    }
    /**
     * 全参构造方法
     *
     * @param endpoint
     * @param accessKeyId
     * @param accessKeySecret
     * @param bucketName
     */
    private AliyunOSS(String endpoint, String accessKeyId, String accessKeySecret, String bucketName) {
        this.endpoint = endpoint;
        this.accessKeyId = accessKeyId;
        this.accessKeySecret = accessKeySecret;
        this.bucketName = bucketName;
        init("Custom");
    }

    /**
     * 初始化，创建OSSClient实例
     */
    private void init(String type) {
        log.info("Hint: Using the {} Configuration To Create Aliyun OSS Client.",type);
        if (Objects.isNull(ossClient)) {
            ossClient = Builder.ossClientBuilder.build(endpoint, accessKeyId, accessKeySecret);
        }
    }

    /**
     * 关闭OSSClient。
     */
    private void destroy() {
        if (null != ossClient) {
            ossClient.shutdown();
        }
    }

    /**
     * 设置bucketName
     *
     * @param bucketName
     */
    public void setBucketName(String bucketName) {
        this.bucketName = bucketName;
    }

    /**
     * static 无参构造方法
     *
     * @return
     */
    public static AliyunOSS me() {
        return new AliyunOSS();
    }

    /**
     * static 全参构造方法
     *
     * @param endpoint
     * @param accessKeyId
     * @param accessKeySecret
     * @param bucketName
     * @return
     */
    public static AliyunOSS me(String endpoint, String accessKeyId, String accessKeySecret, String bucketName) {
        return new AliyunOSS(endpoint, accessKeyId, accessKeySecret, bucketName);
    }

    /**
     * @param
     * @return
     * @description 创建存储空间
     * 存储空间是OSS全局命名空间，相当于数据的容器，可以存储若干文件。 以下代码用于新建一个存储空间
     * @date 2019/11/29 10:30
     */
    public boolean createBucket(String bucketName) {
        if (StrUtil.isEmpty(bucketName)) {
            return false;
        }
        try {
            ossClient.createBucket(bucketName);
            return true;
        } catch (Exception e) {
            log.error("创建存储空间异常: {}", e);
            return false;
        } finally {
            destroy();
        }
    }

    /**
     * @param objectName 上传文件到OSS时需要指定包含文件后缀在内的完整路径，例如abc/efg/123.jpg。
     * @return
     * @description 上传文件，上传文件至OSS
     * @date 2019/11/29 10:31
     */
    public String putObject(String bucketName, String objectName, byte[] content) {
        try {
            // 上传内容到指定的存储空间（bucketName）并保存为指定的文件名称（objectName）。
            ossClient.putObject(bucketName, objectName, new ByteArrayInputStream(content));
            String url = getObjectUrl(bucketName, objectName);
            return url;
        } catch (Exception e) {
            log.error("上传文件异常: {}", e);
            return null;
        } finally {
            destroy();
        }
    }
    public String putObject(String objectName, byte[] content) {
        if (StrUtil.isEmpty(objectName)) {
            return null;
        }
        return putObject(bucketName, objectName, content);
    }

    /**
     * @param
     * @return
     * @description 下载文件，获取文件的文本内容
     * @date 2019/11/29 10:32
     */
    public boolean getObject(String bucketName, String objectName) throws Exception {
        try {
            // 调用ossClient.getObject返回一个OSSObject实例，该实例包含文件内容及文件元信息。
            OSSObject ossObject = ossClient.getObject(bucketName, objectName);
            // 调用ossObject.getObjectContent获取文件输入流，可读取此输入流获取其内容。
            InputStream content = ossObject.getObjectContent();
            if (content != null) {
                BufferedReader reader = new BufferedReader(new InputStreamReader(content));
                while (true) {
                    String line = reader.readLine();
                    if (line == null) break;
                    System.out.println("\n" + line);
                }
                // 数据读取完成后，获取的流必须关闭，否则会造成连接泄漏，导致请求无连接可用，程序无法正常工作。
                content.close();
            }
            return true;
        }catch (Exception e){
            log.error("获取文件异常：{}",e);
            return false;
        }finally {
            destroy();
        }
    }

    /**
     * @param
     * @return
     * @description 列举文件:列举指定存储空间下的文件。默认列举100个文件
     * @date 2019/11/29 10:38
     */
    public void listObjects(String bucketName) {
        // ossClient.listObjects返回ObjectListing实例，包含此次listObject请求的返回结果。
        ObjectListing objectListing = ossClient.listObjects(bucketName);
        // objectListing.getObjectSummaries获取所有文件的描述信息。
        for (OSSObjectSummary objectSummary : objectListing.getObjectSummaries()) {
            System.out.println(" - " + objectSummary.getKey() + "  " + "(size = " + objectSummary.getSize() + ")");
        }

        // 关闭OSSClient。
        destroy();
    }

    /**
     * @param
     * @return
     * @description 删除文件
     * @date 2019/11/29 10:44
     */
    public boolean deleteObject(String bucketName, String objectName) {
        // <yourObjectName>表示删除OSS文件时需要指定包含文件后缀在内的完整路径，例如abc/efg/123.jpg。
        try {
            // 删除文件。
            ossClient.deleteObject(bucketName, objectName);
            return true;
        } catch (Exception e) {
            log.error("删除文件异常: {}", e);
            return false;
        } finally {
            destroy();
        }
    }

    public boolean deleteObject(String objectName) {
        if (StrUtil.isEmpty(objectName)) {
            return false;
        }
        return deleteObject(bucketName, objectName);
    }

    public boolean putFile(String bucketName, String objectName, File file) {
        try {
            // 创建PutObjectRequest对象。
            PutObjectRequest putObjectRequest = new PutObjectRequest(bucketName, objectName, file);
            // 如果需要上传时设置存储类型与访问权限，请参考以下示例代码。
            // ObjectMetadata metadata = new ObjectMetadata();
            // metadata.setHeader(OSSHeaders.OSS_STORAGE_CLASS, StorageClass.Standard.toString());
            // metadata.setObjectAcl(CannedAccessControlList.Private);
            // putObjectRequest.setMetadata(metadata);

            // 上传文件。
            ossClient.putObject(putObjectRequest);
            return true;
        } catch (Exception e) {
            log.error("上传文件异常: {}", e);
            return false;
        } finally {
            destroy();
        }
    }

    public boolean putFile(String objectName, File file) {
        if (StrUtil.isEmpty(objectName)) {
            return false;
        }
        return putFile(bucketName, objectName, file);
    }


    public File getFile(String bucketName, String objectName, File file) {
        try {
            // 下载OSS文件到本地文件。如果指定的本地文件存在会覆盖，不存在则新建。
            ossClient.getObject(new GetObjectRequest(bucketName, objectName), file);
            return file;
        } catch (Exception e) {
            log.error("获取文件异常: {}", e);
            return null;
        } finally {
            destroy();
        }
    }

    public String getObjectUrl(String bucketName, String objectName) {
        if(StrUtil.hasEmpty(endpoint,objectName)){
            return null;
        }
        return String.format("%s/%s",endpoint.replace("//","//" + bucketName + "."),objectName );
    }
}
