package com.alipay.rdf.file.storage;

import com.alipay.rdf.file.exception.RdfErrorEnum;
import com.alipay.rdf.file.exception.RdfFileException;
import com.alipay.rdf.file.interfaces.FileFactory;
import com.alipay.rdf.file.loader.ExtensionLoader;
import com.alipay.rdf.file.model.FileConfig;
import com.alipay.rdf.file.model.FileInfo;
import com.alipay.rdf.file.model.FileSlice;
import com.alipay.rdf.file.model.StorageConfig;
import com.alipay.rdf.file.spi.RdfFileOssClientFactorySpi;
import com.alipay.rdf.file.spi.RdfFileSplitterSpi;
import com.alipay.rdf.file.spi.RdfFileStorageSpi;
import com.alipay.rdf.file.util.RdfFileLogUtil;
import com.alipay.rdf.file.util.RdfFileUtil;
import com.aliyun.oss.ClientException;
import com.aliyun.oss.OSSClient;
import com.aliyun.oss.OSSErrorCode;
import com.aliyun.oss.OSSException;
import com.aliyun.oss.model.*;

import java.io.*;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Pattern;

/**
 * Copyright (C) 2013-2018 Ant Financial Services Group
 *
 * oss 存储操作
 * @author hongwei.quhw
 * @version $Id: FileOssStorage.java, v 0.1 2017年4月7日 下午3:56:04 hongwei.quhw Exp $
 */
public class FileOssStorage implements RdfFileStorageSpi {
    private OssConfig ossConfig;
    private OSSClient client;

    @Override
    public void init(StorageConfig storageConfig) {
        OssConfig config = (OssConfig) storageConfig.getParam(OssConfig.OSS_STORAGE_CONFIG_KEY);
        RdfFileUtil.assertNotNull(config, "rdf-file#StorageConfig中没有传递key="
                                          + OssConfig.OSS_STORAGE_CONFIG_KEY + " 的OssConfig对象参数",
            RdfErrorEnum.ILLEGAL_ARGUMENT);
        RdfFileOssClientFactorySpi factory = ExtensionLoader.getExtensionLoader(RdfFileOssClientFactorySpi.class)
                .getExtension(config.getOssClientFactoryType());
        RdfFileUtil.assertNotNull(factory, "rdf-file#FileOssStorage.init OssClientFactoryType未定义" +
                        ",OssClientFactoryType=" + config.getOssClientFactoryType(),
                RdfErrorEnum.ILLEGAL_ARGUMENT);
        this.client = factory.create(config);
        if (!client.doesBucketExist(config.getBucketName())) {
            client.createBucket(config.getBucketName());
        }
        this.ossConfig = config;
    }

    @Override
    protected void finalize() throws Throwable {
        super.finalize();
        if (null != client) {
            client.shutdown();
            if (RdfFileLogUtil.common.isInfo()) {
                RdfFileLogUtil.common.info("rdf-file#FileOssStorage.finalize ");
            }
        }
    }

    /**
     * @see com.alipay.rdf.file.storage.FileInnterStorage#getInputStream(java.lang.String)
     */
    @Override
    public InputStream getInputStream(String filePath) {
        filePath = toOSSPath(filePath);
        if (isExist(filePath)) {
            OSSObject object = client.getObject(ossConfig.getBucketName(), filePath);
            if (RdfFileLogUtil.common.isInfo()) {
                RdfFileLogUtil.common.info("rdf-file#FileOssStorage.getInputStream requestId="
                                           + object.getRequestId() + ", filePath=" + filePath);
            }
            return new OssInputStream(object);
        } else {
            throw new RdfFileException(
                "rdf-file#FileOssStorage.getInputStream(filePath=" + filePath + "), 文件不存在",
                RdfErrorEnum.NOT_EXSIT);
        }
    }

    /**
     * @see com.alipay.rdf.file.storage.FileInnterStorage#getInputStream(java.lang.String, long, long)
     */
    @Override
    public InputStream getInputStream(String filePath, long start, long length) {
        if (length <= 0) {
            if (ossConfig.isEmptyLeZero()) {
                // 跟nas（本地磁盘）读取保持一致行为， 需要用户通过参数强制指定开启
                return new OssInputStream(null);
            }

            throw new RdfFileException("rdf-file#FileOssStorage.getInputStream(filePath=" + filePath
                                       + ", start=" + start + ", length=" + length + "获取数据长度必须大于零",
                RdfErrorEnum.ILLEGAL_ARGUMENT);
        }

        filePath = toOSSPath(filePath);
        if (isExist(filePath)) {
            GetObjectRequest getObjectRequest = new GetObjectRequest(ossConfig.getBucketName(),
                filePath);
            getObjectRequest.setRange(start, start + length - 1);
            OSSObject object = client.getObject(getObjectRequest);
            if (RdfFileLogUtil.common.isInfo()) {
                RdfFileLogUtil.common
                    .info(
                        "rdf-file#FileOssStorage.getInputStream requestId=" + object.getRequestId()
                          + ", filePath=" + filePath + ", start=" + start + ", length=" + length);
            }
            return new OssInputStream(object);
        } else {
            throw new RdfFileException("rdf-file#FileOssStorage.getInputStream(filePath=" + filePath
                                       + ", start=" + start + ", length=" + length + ")",
                RdfErrorEnum.NOT_EXSIT);
        }
    }

    /**
     * @see com.alipay.rdf.file.storage.FileInnterStorage#getTailInputStream(com.alipay.rdf.file.model.FileConfig)
     */
    @Override
    public InputStream getTailInputStream(FileConfig fileConfig) {
        RdfFileSplitterSpi fileSplitter = (RdfFileSplitterSpi) FileFactory
            .createSplitter(fileConfig.getStorageConfig());
        FileSlice fileSlice = fileSplitter.getTailSlice(fileConfig);
        if (null == fileSlice) {
            return null;
        }
        return getInputStream(fileConfig.getFilePath(), fileSlice.getStart(),
            fileSlice.getLength());
    }

    /**
     * @see com.alipay.rdf.file.interfaces.FileStorage#createNewFile(java.lang.String)
     */
    @Override
    public void createNewFile(String filePath) {
        filePath = toOSSPath(filePath);

        ObjectMetadata objectMeta = new ObjectMetadata();
        byte[] buffer = new byte[0];
        ByteArrayInputStream in = new ByteArrayInputStream(buffer);
        objectMeta.setContentLength(0);
        try {
            PutObjectResult result = client.putObject(ossConfig.getBucketName(), filePath, in,
                objectMeta);
            if (RdfFileLogUtil.common.isInfo()) {
                RdfFileLogUtil.common.info("rdf-file#FileOssStorage.getInputStream requestId="
                                           + result.getRequestId() + ", filePath=" + filePath);
            }
        } catch (Exception e) {
            throw new RdfFileException("rdf-file#创建OSS文件异常！", e, RdfErrorEnum.IO_ERROR);
        } finally {
            try {
                in.close();
            } catch (IOException e) {
                if (RdfFileLogUtil.common.isWarn()) {
                    RdfFileLogUtil.common.warn("rdf-file#创建OSS文件时关闭流异常！", e);
                }
            }
        }
    }

    /**
     * @see com.alipay.rdf.file.interfaces.FileStorage#getFileInfo(java.lang.String)
     */
    @Override
    public FileInfo getFileInfo(String filePath) {
        filePath = toOSSPath(filePath);
        FileInfo fileInfo = new FileInfo();
        try {
            ObjectMetadata metaData = client.getObjectMetadata(ossConfig.getBucketName(), filePath);
            fileInfo.setFileName(new File(filePath).getName());
            fileInfo.setLastModifiedDate(metaData.getLastModified());
            fileInfo.setSize(metaData.getContentLength());
            fileInfo.setExists(true);
            fileInfo.getMetadata().putAll(metaData.getRawMetadata());
            fileInfo.getUserMetadata().putAll(metaData.getUserMetadata());

            if (RdfFileLogUtil.common.isInfo()) {
                RdfFileLogUtil.common
                    .info("rdf-file#FileOssStorage.copy requestId=" + metaData.getRequestId());
            }
        } catch (OSSException e) {
            if (OSSErrorCode.NO_SUCH_KEY.equals(e.getErrorCode())) {
                fileInfo.setExists(false);
            } else {
                throw e;
            }
        }
        return fileInfo;
    }

    /**
     * @see com.alipay.rdf.file.interfaces.FileStorage#listFiles(java.lang.String, java.lang.String[])
     */
    @Override
    public List<String> listFiles(String folderName, String[] regexs) {
        return listFilesWithRegex(folderName, regexs, false);
    }

    /**
     * @see com.alipay.rdf.file.interfaces.FileStorage#listFiles(java.lang.String, com.alipay.rdf.file.interfaces.FileStorage.FilePathFilter[])
     */
    @Override
    public List<String> listFiles(String folderName, FilePathFilter... fileFilters) {
        return listFilesWithFilter(folderName, false, fileFilters);
    }

    /**
     * helper method for adding FilePathFilter check when listing files
     *
     * @param folderName
     * @param all
     * @param fileFilters
     * @return
     */
    private List<String> listFilesWithFilter(String folderName, boolean all,
                                             FilePathFilter... fileFilters) {
        List<String> filePaths = listFilesHandler(folderName, null, all);
        if (fileFilters == null || fileFilters.length == 0) {
            return filePaths;
        }
        List<String> result = new ArrayList<String>();
        for (String path : filePaths) {
            for (FilePathFilter filter : fileFilters) {
                if (filter.accept(path)) {
                    result.add(path);
                    break;
                }
            }
        }
        return result;
    }

    /**
     * @see com.alipay.rdf.file.interfaces.FileStorage#listAllFiles(java.lang.String, java.lang.String[])
     */
    @Override
    public List<String> listAllFiles(String folderName, String[] regexs) {
        return listFilesWithRegex(folderName, regexs, true);
    }

    /**
     * @see com.alipay.rdf.file.interfaces.FileStorage#listAllFiles(java.lang.String, com.alipay.rdf.file.interfaces.FileStorage.FilePathFilter[])
     */
    @Override
    public List<String> listAllFiles(String folderName, FilePathFilter... fileFilters) {
        return listFilesWithFilter(folderName, true, fileFilters);
    }

    /**
     * @see com.alipay.rdf.file.interfaces.FileStorage#download(java.lang.String, java.lang.String)
     */
    @Override
    public void download(String srcOSSPath, String toFile) {
        srcOSSPath = toOSSPath(srcOSSPath);
        List<String> fileNames = listAllFiles(srcOSSPath);
        if(fileNames.isEmpty()){
            // 以下两种情况需要抛出异常：
            // 1.单个文件场景且文件未找到
            // 2.文件夹场景但文件夹下没有任何文件(可能是文件夹名称错误也可能是事实上该文件夹下就没有文件，因为oss无法区分这两种情况)
            if(!isExist(srcOSSPath)){
                throw new RdfFileException("rdf-file# oss donwLoad srcOSSPath=" + srcOSSPath + " 不存在",
                        RdfErrorEnum.NOT_EXSIT);
            }
            fileNames.add(srcOSSPath);
        }
        String temp = "";
        for (String name : fileNames) {
            if (!name.equals(srcOSSPath)) {
                toFile = toFile.replaceAll("\\\\", "/");
                if (toFile.endsWith("/")) {
                    toFile = toFile.substring(0, toFile.length() - 1);
                }
                String subFileName = name.substring(name.indexOf(srcOSSPath) + srcOSSPath.length());
                if (subFileName.startsWith("/")) {
                    subFileName = subFileName.substring(1);
                }
                temp = toFile + "/" + subFileName;
            } else {
                //srcOSSPath为单个文件，不为文件夹
                temp = toFile;
            }
            downloadFile(this.client, this.ossConfig.getBucketName(), name, temp);
        }
    }

    /**
     * @see com.alipay.rdf.file.interfaces.FileStorage#upload(java.lang.String, java.lang.String, boolean)
     */
    @Override
    public void upload(String srcFile, String toFile, boolean override) {
        File file = new File(srcFile);
        if (!file.exists()) {
            throw new RdfFileException("rdf-file#上传至OSS时本地文件不存在！ srcPath=" + srcFile,
                RdfErrorEnum.NOT_EXSIT);
        }
        srcFile = file.getAbsolutePath().replaceAll("\\\\", "/");
        toFile = toOSSPath(toFile);

        if (file.isDirectory()) {
            File[] files = file.listFiles();
            if (files != null && files.length > 0) {
                for (File f : files) {
                    String temp = f.getAbsolutePath().replaceAll("\\\\", "/");
                    if (!RdfFileUtil.equals(srcFile, toFile)) {
                        if (toFile.endsWith("/")) {
                            toFile = toFile.substring(0, toFile.length() - 1);
                        }
                        temp = toFile + "/" + f.getName();
                    }
                    upload(f.getAbsolutePath(), temp, override);
                }
            }
        } else {
            if (file.length() < ossConfig.getOssBigFileSize()) {
                uploadFile(file, toFile, override, null);
            } else {
                uploadBigFile(srcFile, toFile, override, null);
            }
        }
    }

    /**
     * @see com.alipay.rdf.file.interfaces.FileStorage#rename(java.lang.String, java.lang.String)
     */
    @Override
    public void rename(String srcFile, String toFile) {
        srcFile = toOSSPath(srcFile);
        toFile = toOSSPath(toFile);
        //若目标路径为空，则移动源文件到目标路径处，若存在，则覆盖
        if (isExist(toFile)) {
            delete(toFile);
        }
        copy(srcFile, toFile);

        // 删除原文件
        delete(srcFile);
    }

    /**
     * @see com.alipay.rdf.file.interfaces.FileStorage#copy(java.lang.String, java.lang.String)
     */
    @Override
    public void copy(String srcFile, String toFile) {
        copy(ossConfig.getBucketName(), srcFile, ossConfig.getBucketName(), toFile);
    }

    /**
     * @see com.alipay.rdf.file.interfaces.FileStorage#delete(java.lang.String)
     */
    @Override
    public void delete(String fullPath) {
        fullPath = toOSSPath(fullPath);
        try {
            List<String> fileNames = listAllFiles(fullPath);
            if (fileNames.size() > 0) {
                for (String name : fileNames) {
                    client.deleteObject(ossConfig.getBucketName(), name);
                }
            } else {
                client.deleteObject(ossConfig.getBucketName(), fullPath);
            }
        } catch (Exception e) {
            throw new RdfFileException("删除OSS文件异常", e, RdfErrorEnum.IO_ERROR);
        }
    }

    /**
     * 拷贝到另一个bucket
     *
     * @param srcFile
     * @param toBucketName
     * @param toFile
     */
    public void copy(String srcBucketName, String srcFile, String toBucketName, String toFile) {
        srcFile = toOSSPath(srcFile);
        toFile = toOSSPath(toFile);

        FileInfo fileInfo = getFileInfo(srcFile);
        if (fileInfo.getSize() < ossConfig.getOssBigFileSize()) {
            CopyObjectResult result = client.copyObject(srcBucketName, srcFile, toBucketName,
                toFile);
            if (RdfFileLogUtil.common.isInfo()) {
                RdfFileLogUtil.common
                    .info("rdf-file#FileOssStorage.copy requestId=" + result.getRequestId());
            }
        } else {
            copyBigFile(srcBucketName, srcFile, toBucketName, toFile, fileInfo);
        }
    }

    private void copyBigFile(String srcBucketName, String srcFile, String toBucketName,
                             String toFile, FileInfo fileInfo) {
        long contentLength = fileInfo.getSize();
        // 分片大小，10MB
        long partSize = 1024 * 1024 * 10;
        // 计算分块数目
        int partCount = (int) (contentLength / partSize);
        if (contentLength % partSize != 0) {
            partCount++;
        }

        // 初始化拷贝任务
        InitiateMultipartUploadRequest initiateMultipartUploadRequest = new InitiateMultipartUploadRequest(
            toBucketName, toFile);
        InitiateMultipartUploadResult initiateMultipartUploadResult = client
            .initiateMultipartUpload(initiateMultipartUploadRequest);
        String uploadId = initiateMultipartUploadResult.getUploadId();

        // 分片拷贝
        List<PartETag> partETags = new ArrayList<PartETag>();
        for (int i = 0; i < partCount; i++) {
            // 计算每个分块的大小
            long skipBytes = partSize * i;
            long size = partSize < contentLength - skipBytes ? partSize : contentLength - skipBytes;
            // 创建UploadPartCopyRequest
            UploadPartCopyRequest uploadPartCopyRequest = new UploadPartCopyRequest(srcBucketName,
                srcFile, toBucketName, toFile);
            uploadPartCopyRequest.setUploadId(uploadId);
            uploadPartCopyRequest.setPartSize(size);
            uploadPartCopyRequest.setBeginIndex(skipBytes);
            uploadPartCopyRequest.setPartNumber(i + 1);
            UploadPartCopyResult uploadPartCopyResult = client
                .uploadPartCopy(uploadPartCopyRequest);
            // 将返回的PartETag保存到List中
            partETags.add(uploadPartCopyResult.getPartETag());
        }

        // 提交分片拷贝任务
        CompleteMultipartUploadRequest completeMultipartUploadRequest = new CompleteMultipartUploadRequest(
            toBucketName, toFile, uploadId, partETags);
        CompleteMultipartUploadResult result = client
            .completeMultipartUpload(completeMultipartUploadRequest);
        if (RdfFileLogUtil.common.isInfo()) {
            RdfFileLogUtil.common
                .info("rdf-file#FileOssStorage.copyBigFile requestId=" + result.getRequestId());
        }
    }

    /**
     * 追加上传
     */
    public void appendUploadFile(String srcFile, String ossFilePath) {
        File file = new File(srcFile);
        if (!file.exists()) {
            throw new RdfFileException("rdf-file#上传至OSS时本地文件不存在！ srcPath=" + srcFile,
                RdfErrorEnum.NOT_EXSIT);
        }
        ossFilePath = toOSSPath(ossFilePath);

        long position = 0;
        FileInfo fileInfo = getFileInfo(ossFilePath);
        if (fileInfo.isExists()) {
            position = fileInfo.getSize();
        }

        if (position + file.length() > ossConfig.getOssAppendSizeLimit()) {
            throw new RdfFileException(
                "rdf-file# oss append upload file srcFile=" + srcFile + " size=" + file.length()
                                       + ", ossFile=" + ossFilePath + " size=" + position
                                       + ", 总和大于append大小限制=" + ossConfig.getOssAppendSizeLimit(),
                RdfErrorEnum.VALIDATE_ERROR);
        }

        AppendObjectRequest appendObjectRequest = new AppendObjectRequest(ossConfig.getBucketName(),
            ossFilePath, file);
        appendObjectRequest.setPosition(position);

        AppendObjectResult result = client.appendObject(appendObjectRequest);

        if (RdfFileLogUtil.common.isInfo()) {
            RdfFileLogUtil.common
                .info("rdf-file#FileOssStorage.appendUploadFile(srcFile=" + srcFile
                      + ",ossFilePath=" + ossFilePath + ") 上传objectCRC=" + result.getObjectCRC()
                      + ", requestId=" + result.getRequestId());
        }
    }

    /**
     * 上传一个文件至指定路径
     *
     * @param file
     * @param ossFilePath
     * @param override
     * @param MD5           可为空
     */
    private void uploadFile(File file, String ossFilePath, boolean override, String MD5) {
        ossFilePath = toOSSPath(ossFilePath);
        //若目标路径已存在文件，且不覆盖，则停止上传
        if (!override && isExist(ossFilePath)) {
            return;
        }
        //若覆盖，则先删除目标路径下的文件
        if (override) {
            client.deleteObject(ossConfig.getBucketName(), ossFilePath);
        }

        InputStream fileStream = null;
        try {
            fileStream = new FileInputStream(file);
        } catch (FileNotFoundException e1) {
            throw new RdfFileException("rdf-file#上传文件出现异常，file路径为" + file.getAbsolutePath(),
                RdfErrorEnum.NOT_EXSIT);
        }
        ObjectMetadata meta = new ObjectMetadata();
        if (RdfFileUtil.isNotBlank(MD5)) {
            meta.setContentMD5(MD5);
        }
        try {
            PutObjectResult result = client.putObject(ossConfig.getBucketName(), ossFilePath,
                fileStream, meta);
            if (RdfFileLogUtil.common.isInfo()) {
                RdfFileLogUtil.common.info("rdf-file#FileOssStorage.uploadFile requestId="
                                           + result.getRequestId() + ", filePath=" + ossFilePath);
            }
        } finally {
            if (null != fileStream) {
                try {
                    fileStream.close();
                } catch (IOException e) {
                    //忽略异常
                    if (RdfFileLogUtil.common.isWarn()) {
                        RdfFileLogUtil.common
                            .warn("rdf-file#oss upload file close io error ignore.", e);
                    }
                }
            }
        }
    }

    /**
     * 上传一个大文件至oss指定路径
     *
     * @param srcPath
     * @param ossFilePath
     * @param override
     * @param MD5
     */
    private void uploadBigFile(String srcPath, String ossFilePath, boolean override, String MD5) {
        ossFilePath = toOSSPath(ossFilePath);
        //若目标路径已存在文件，且不需要覆盖，则停止上传
        if (!override && isExist(ossFilePath)) {
            return;
        }
        //若需要覆盖，则先删除目标路径下的文件
        if (override) {
            client.deleteObject(ossConfig.getBucketName(), ossFilePath);
        }

        UploadFileRequest uploadFileRequest = new UploadFileRequest(ossConfig.getBucketName(),
            ossFilePath);

        uploadFileRequest.setUploadFile(srcPath);
        uploadFileRequest.setTaskNum(5);
        uploadFileRequest.setPartSize(10 * 1024 * 1024);
        uploadFileRequest.setEnableCheckpoint(true);

        if (RdfFileUtil.isNotBlank(MD5)) {
            ObjectMetadata meta = new ObjectMetadata();
            meta.setContentMD5(MD5);
            uploadFileRequest.setObjectMetadata(meta);
        }

        try {
            client.uploadFile(uploadFileRequest);
        } catch (Throwable e) {
            throw new RdfFileException("rdf-file#oss上传文件出现异常", e, RdfErrorEnum.IO_ERROR);
        }
    }

    /**
     * download one file
     *
     * @param client
     * @param bucketName
     * @param filename
     * @param localFilename
     */
    private void downloadFile(OSSClient client, String bucketName, String filename,
                              String localFilename) {
        filename = this.toOSSPath(filename);
        GetObjectRequest getObjectRequest = new GetObjectRequest(bucketName, filename);
        File localFile = new File(localFilename);
        if (!localFile.getParentFile().exists()) {
            localFile.getParentFile().mkdirs();
        }
        ObjectMetadata result = client.getObject(getObjectRequest, localFile);
        if (RdfFileLogUtil.common.isInfo()) {
            RdfFileLogUtil.common.info("rdf-file#FileOssStorage.downloadFile requestId="
                                       + result.getRequestId() + ", filePath=" + filename);
        }
    }

    /**
     * helper method for adding regex check when listing files
     *
     * @param folderName
     * @param regexs
     * @param all
     * @return
     */
    private List<String> listFilesWithRegex(String folderName, String[] regexs, boolean all) {
        List<String> filePaths = listFilesHandler(folderName, null, all);
        if (regexs == null || regexs.length == 0) {
            return filePaths;
        }
        List<String> result = new ArrayList<String>();
        for (String filePath : filePaths) {
            String temp = "";
            if (filePath.endsWith("/")) {
                //文件夹
                temp = filePath.substring(0, filePath.length() - 1);
            } else {
                temp = filePath;
            }
            String name = temp.substring(temp.lastIndexOf("/") + 1, temp.length());
            for (String regex : regexs) {
                Pattern pattern = Pattern.compile(regex);
                if (pattern.matcher(name).matches()) {
                    result.add(filePath);
                    break;
                }
            }
        }
        return result;
    }

    /**
     * handler method for listing files
     *
     * @param folderName
     * @param marker
     * @param all
     * @return
     */
    private List<String> listFilesHandler(String folderName, String marker, boolean all) {
        folderName = toOSSPath(folderName);
        if (!folderName.endsWith("/")) {
            folderName += "/";
        }
        List<String> filePaths = new ArrayList<String>();
        ListObjectsRequest request = new ListObjectsRequest();
        request.setPrefix(folderName);
        request.setBucketName(ossConfig.getBucketName());
        if (!all) {
            request.setDelimiter("/");
        }
        if (RdfFileUtil.isNotBlank(marker)) {
            request.setMarker(marker);
        }
        ObjectListing result = client.listObjects(request);
        for (OSSObjectSummary object : result.getObjectSummaries()) {
            if (all && object.getKey().endsWith("/")) {
                continue;
            }
            String path = object.getKey();
            if (RdfFileUtil.equals(folderName, path)) {
                continue;
            }
            if (!RdfFileUtil.isBlank(path)) {
                filePaths.add(path);
            }
        }
        if (!all) {
            for (String commonPrefix : result.getCommonPrefixes()) {
                filePaths.add(commonPrefix);
            }
        }
        if (result.isTruncated()) {
            filePaths.addAll(listFilesHandler(folderName, result.getNextMarker(), all));
        }
        return filePaths;

    }

    /**
     * 判断oss某路径下是否存在文件
     *
     * @param ossFilePath
     * @return
     */
    private boolean isExist(String ossFilePath) throws OSSException, ClientException {
        ossFilePath = toOSSPath(ossFilePath);
        try {
            return client.doesObjectExist(ossConfig.getBucketName(), ossFilePath);
        } catch (OSSException e) {
            if (e.getErrorCode() == OSSErrorCode.NO_SUCH_BUCKET
                || e.getErrorCode() == OSSErrorCode.NO_SUCH_KEY) {
                return false;
            }
            throw e;
        }
    }

    /**
     * oss 存储路径不能以/开始
     *
     * @param filePath
     * @return
     */
    private String toOSSPath(String filePath) {
        filePath = filePath.replaceAll("\\\\", "/");

        if (filePath.startsWith("/")) {
            filePath = filePath.substring(1);
        }
        return filePath;
    }
}
