<?php

/**
 * 阿里云对象存储 OSS
 * @package model
 */

include_once (SERVER_ROOT . "/model/mBase.php");

include_once SERVER_ROOT . "/library/aliyun/OSS/OssClient.php";

class mAliyunOSS extends mBase{

    // oss节点
    public $endpoint = ALIYUN_OSS_END_POINT;

    /**
     * 判断存储空间bucket是否存在
     * @param unknown $bucket  存储空间
     * @return boolean
     */
    public function isExistBucket($bucket) {
        try {
            $ossClient = new OssClient(ALIYUN_OSS_ACCESS_KEY_ID, ALIYUN_OSS_ACCESS_KEY_SECRET, $this->endpoint);
            $res = $ossClient->doesBucketExist($bucket);
        } catch (OssException $e) {
            $this->setError($e->getMessage());
            return false;
        }
        if ($res === false) return false;
        return true;
    }

    /**
     * 列举存储空间
     * @return boolean|string
     */
    public function getBucketList() {
        try{
            $ossClient = new OssClient(ALIYUN_OSS_ACCESS_KEY_ID, ALIYUN_OSS_ACCESS_KEY_SECRET, $this->endpoint);
            $bucketListInfo = $ossClient->listBuckets();
        } catch(OssException $e) {
            $this->setError($e->getMessage());
            return false;
        }
        $bucketList = $bucketListInfo->getBucketList();

        $nlist = array();
        foreach($bucketList as $key => $bucket) {
            $nlist[$key]['location'] = $bucket->getLocation();
            $nlist[$key]['name'] = $bucket->getName();
            $nlist[$key]['create_date'] = $bucket->getCreatedate();
        }
        return $nlist;
    }

    /**
     * 简单上传 上传字符串
     * @param unknown $bucket           存储空间名称
     * @param unknown $oss_save_path    oss下文件路径及名称
     * @param unknown $content          字符串内容
     */
    public function simpleUploadStr($bucket, $oss_save_path, $content) {
        try{
            $ossClient = new OssClient(ALIYUN_OSS_ACCESS_KEY_ID, ALIYUN_OSS_ACCESS_KEY_SECRET, $this->endpoint);

            $ossClient->putObject($bucket, $oss_save_path, $content);
        } catch(OssException $e) {
            $this->setError($e->getMessage());
            return false;
        }
        return true;
    }

    /**
     * 简单上传 上传文件
     * @param unknown $bucket           存储空间名称
     * @param unknown $oss_save_path    oss下文件路径及名称
     * @param unknown $upload_file_path 需要上传到oss文件的路径
     */
    public function simpleUploadFile($bucket, $oss_save_path, $upload_file_path) {
        try{
            $ossClient = new OssClient(ALIYUN_OSS_ACCESS_KEY_ID, ALIYUN_OSS_ACCESS_KEY_SECRET, $this->endpoint);

            $ossClient->uploadFile($bucket, $oss_save_path, $upload_file_path);
        } catch(OssException $e) {
            $this->setError($e->getMessage());
            return false;
        }
        return true;
    }

    /**
     * 分片上传
     * @param unknown $bucket               存储空间名称
     * @param unknown $oss_save_path        oss下文件路径及名称
     * @param unknown $upload_file_path     需要上传到oss文件的路径
     * @return boolean
     */
    public function multiPartUpload($bucket, $oss_save_path, $upload_file_path) {
        /**
         *  步骤1:初始化一个分片上传事件,获取uploadId。
         */
        try{
            $ossClient = new OssClient(ALIYUN_OSS_ACCESS_KEY_ID, ALIYUN_OSS_ACCESS_KEY_SECRET, $this->endpoint);

            //返回uploadId,它是分片上传事件的唯一标识,您可以根据这个ID来发起相关的操作,如取消分片上传、查询分片上传等。
            $uploadId = $ossClient->initiateMultipartUpload($bucket, $oss_save_path);
        } catch(OssException $e) {
            $this->setError($e->getMessage());
            return false;
        }

        /*
         * 步骤2:上传分片。
         */
        $partSize = ALIYUN_OSS_PARTS_UPLOAD_SIZE * 1024 * 1024;
        $uploadFileSize = filesize($upload_file_path);
        $pieces = $ossClient->generateMultiuploadParts($uploadFileSize, $partSize);
        $responseUploadPart = array();
        $uploadPosition = 0;
        $isCheckMd5 = true;
        foreach ($pieces as $i => $piece) {
            $fromPos = $uploadPosition + (integer)$piece[$ossClient::OSS_SEEK_TO];
            $toPos = (integer)$piece[$ossClient::OSS_LENGTH] + $fromPos - 1;
            $upOptions = array(
                $ossClient::OSS_FILE_UPLOAD => $upload_file_path,
                $ossClient::OSS_PART_NUM => ($i + 1),
                $ossClient::OSS_SEEK_TO => $fromPos,
                $ossClient::OSS_LENGTH => $toPos - $fromPos + 1,
                $ossClient::OSS_CHECK_MD5 => $isCheckMd5,
            );
            // MD5校验。
            if ($isCheckMd5) {
                $contentMd5 = OssUtil::getMd5SumForFile($upload_file_path, $fromPos, $toPos);
                $upOptions[$ossClient::OSS_CONTENT_MD5] = $contentMd5;
            }
            try {
                // 上传分片。
                $responseUploadPart[] = $ossClient->uploadPart($bucket, $oss_save_path, $uploadId, $upOptions);
            } catch(OssException $e) {
                $this->setError($e->getMessage());
                return false;
            }
        }
        // $uploadParts是由每个分片的ETag和分片号(PartNumber)组成的数组。
        $uploadParts = array();
        foreach ($responseUploadPart as $i => $eTag) {
            $uploadParts[] = array(
                'PartNumber' => ($i + 1),
                'ETag' => $eTag,
            );
        }
        /**
         * 步骤3:完成上传。
         */
        try {
            // 在执行该操作时,需要提供所有有效的$uploadParts。OSS收到提交的$uploadParts后,会逐一验证每个分片的有效性。当所有的数据分片验证通过后,OSS将把这些分片组合成一个完整的文件。
            $ossClient->completeMultipartUpload($bucket, $oss_save_path, $uploadId, $uploadParts);
        }  catch(OssException $e) {
            $this->setError($e->getMessage());
            return false;
        }
        return true;
    }

    /**
     * 上传一个目录下所有文件
     * @param unknown $bucket           存储空间名称
     * @param unknown $dir              本地目录,目录下只能是文件   如/a/b/1.txt, /a/b/2.txt, /a/b/c/3.txt   dir='/a/b' c目录不会上传
     * @param unknown $oss_save_path    需要保存到oss路径
     */
    public function multiPartUploadDir($bucket, $dir, $oss_save_path) {
        try {
            $ossClient = new OssClient(ALIYUN_OSS_ACCESS_KEY_ID, ALIYUN_OSS_ACCESS_KEY_SECRET, $this->endpoint);

            $ossClient->uploadDir($bucket, $oss_save_path, $dir);
        }  catch(OssException $e) {
            $this->setError($e->getMessage());
            return false;
        }
        return true;
    }

    /**
     * 下载文件到本地
     * @param unknown $bucket           存储空间名称
     * @param unknown $oss_save_path    oss下文件路径及名称
     * @param unknown $local_path       保存到本地路径
     */
    public function downloadToLocal($bucket, $oss_save_path, $local_path) {
        $dir = dirname($local_path);
        if(!is_dir($dir)) {
            mkdir($dir, 0775, true);
            chown($dir, 'nobody');
            chgrp($dir, 'nobody');
        }

        $options = array(
            OssClient::OSS_FILE_DOWNLOAD => $local_path
        );

        try{
            $ossClient = new OssClient(ALIYUN_OSS_ACCESS_KEY_ID, ALIYUN_OSS_ACCESS_KEY_SECRET, $this->endpoint);
            $ossClient->getObject($bucket, $oss_save_path, $options);
        } catch(OssException $e) {
            $this->setError($e->getMessage());
            return false;
        }
        return true;
    }

    /**
     * 下载文件到本地内存
     * @param unknown $bucket           存储空间名称
     * @param unknown $oss_save_path    oss下文件路径及名称 如abc/efg/123.jpg
     * @return void|string
     */
    public function downloadToLocalMemory($bucket, $oss_save_path) {
        try{
            $ossClient = new OssClient(ALIYUN_OSS_ACCESS_KEY_ID, ALIYUN_OSS_ACCESS_KEY_SECRET, $this->endpoint);

            $content = $ossClient->getObject($bucket, $oss_save_path);
        } catch(OssException $e) {
            printf(__FUNCTION__ . ": FAILED\n");
            printf($e->getMessage() . "\n");
            return;
        }
        return $content;
    }

    /**
     * 删除单个文件
     * @param unknown $bucket           存储空间名称
     * @param unknown $oss_save_path    oss下文件路径及名称 如abc/efg/123.jpg
     * @return boolean
     */
    public function delSingleFile($bucket, $oss_save_path) {
        try{
            $ossClient = new OssClient(ALIYUN_OSS_ACCESS_KEY_ID, ALIYUN_OSS_ACCESS_KEY_SECRET, $this->endpoint);

            $ossClient->deleteObject($bucket, $oss_save_path);
        } catch(OssException $e) {
            $this->setError($e->getMessage());
            return false;
        }
        return true;
    }

    /**
     * 删除多个文件
     * @param unknown $bucket                 存储空间名称
     * @param unknown $oss_save_path_files    oss下文件路径及名称数组 如array('abc/efg/123.jpg', 'abc/efg/234.jpg')
     * @return boolean
     */
    public function delMultiFile($bucket, $oss_save_path_files) {
        try{
            $ossClient = new OssClient(ALIYUN_OSS_ACCESS_KEY_ID, ALIYUN_OSS_ACCESS_KEY_SECRET, $this->endpoint);

            $ossClient->deleteObjects($bucket, $oss_save_path_files);
        } catch(OssException $e) {
            $this->setError($e->getMessage());
            return false;
        }
        return true;
    }


    /**
     * 列举文件列表
     * @param unknown $bucket       存储空间名称
     * @param unknown $oss_dir      oss存储目录 为空列举所有文件
     * @param unknown $marker       下一次查询标记(文件名)
     * @param number $limit         每次查询数量
     */
    public function getOssFileList($bucket, $oss_dir=null, $marker=null, $limit=10) {
        $prefix = $oss_dir;
        $delimiter = '';
        $nextMarker = $marker;
        $maxkeys = $limit;

        $options = array(
            'prefix'    => $prefix,
            'delimiter' => $delimiter,
            'max-keys'  => $maxkeys,
            'marker'    => $nextMarker,
        );
        // prefix
        // 对Object名字进行分组的字符。
        // 所有Object名字包含指定的前缀,第一次出现Delimiter字符之间的Object作为一组元素(即CommonPrefixes)。

        // delimiter
        // 限定返回文件的Key必须以Prefix作为前缀。
        // 如果把Prefix设为某个文件夹名,就可以列举以此Prefix开头的文件,即该文件夹下递归的所有文件和子文件夹。
        // 在设置Prefix的基础上增加设置Delimiter为 / 时,返回值就只列举该文件夹下的文件,文件夹下的子文件夹名返回在CommonPrefixes中,子文件夹下递归的所有文件和文件夹不显示。
        // 例如,一个Bucket中存在三个Object : fun/test.jpg, fun/movie/001.avi,fun/movie/007.avi。若设定Prefix为 fun/,则返回三个Object;如果增加设定Delimiter为 /,则返回fun/test.jpg和fun/movie/ 。
        // 参数的长度必须小于1024字节。使用Prefix查询时,返回的Key中仍会包含Prefix。

        // marker
        // 设定从Marker之后按字母排序开始返回Object。
        // Marker用来实现分页显示效果,参数的长度必须小于1024字节。在做条件查询时,即使Marker在列表中不存在,也会从符合Marker字母排序的下一个开始打印。

        // max-keys
        // 指定返回Object的最大数。
        // 取值:大于0小于1000,默认值:100。
        // 如果因为Max-keys的设定无法一次完成列举,返回结果会附加一个<NextMarker>作为下一次列举的Marker。


        try {
            $ossClient = new OssClient(ALIYUN_OSS_ACCESS_KEY_ID, ALIYUN_OSS_ACCESS_KEY_SECRET, $this->endpoint);
            $listObjectInfo = $ossClient->listObjects($bucket, $options);
        } catch (OssException $e) {
            $this->setError($e->getMessage());
            return false;
        }

        $nlist['bucketName'] = $listObjectInfo->getBucketName();
        $nlist['prefix'] = $listObjectInfo->getPrefix();
        $nlist['marker'] = $listObjectInfo->getMarker();
        $nlist['nextMarker'] = $listObjectInfo->getNextMarker();
        $nlist['maxKeys'] = $listObjectInfo->getMaxKeys();
        $nlist['delimiter'] = $listObjectInfo->getDelimiter();
        $nlist['isTruncated'] = $listObjectInfo->getIsTruncated();
        $nlist['objectList'] = array();
        $nlist['prefixList'] = array();

        $objectList = $listObjectInfo->getObjectList(); // object list
        $prefixList = $listObjectInfo->getPrefixList(); // directory list
        if (!empty($objectList)) {
            foreach ($objectList as $objectInfo) {
                $nlist['objectList'][] = $objectInfo->getKey();
            }
        }
        if (!empty($prefixList)) {
            foreach ($prefixList as $prefixInfo) {
                $nlist['prefixList'][] = $prefixInfo->getPrefix();
            }
        }
        return $nlist;
    }

    /**
     * 上传文件到oss
     * @param unknown $bucket               存储空间名称
     * @param unknown $oss_save_path        oss下文件路径及名称
     * @param unknown $upload_file_path     需要上传到oss文件的路径
     * @return boolean
     */
    public function uploadFileToOss($bucket, $oss_save_path, $upload_file_path) {
        $file_size = filesize($upload_file_path);

        if($file_size>ALIYUN_OSS_PARTS_UPLOAD_SIZE) {
            $res = $this->multiPartUpload($bucket, $oss_save_path, $upload_file_path);
        } else {
            $res = $this->simpleUploadFile($bucket, $oss_save_path, $upload_file_path);
        }

        if(!$res) {
            $this->setError($this->getError());
            return false;
        }

        return true;
    }

    public function downFileFromOss($oss_type, $bucket, $oss_dir=null, $marker=null, $limit=1) {
        $list = $this->getOssFileList($bucket, $oss_dir, $marker, $limit);

        foreach ($list['objectList'] as $file) {
            $local_path = $this->getLocalSavePath($oss_type, $file);
            $res = $this->downloadToLocal($bucket, $file, $local_path);

            // 首页私有模板 需要解压
            if($oss_type == OSS_FILE_PRI_TPL) {
                $dir = dirname($local_path);
                $pobj = new mPaper();
                $pobj->unZipRar($local_path, $dir);
            }
        }

        if($list['isTruncated']=='false') return true;

        $this->downFileFromOss($oss_type, $bucket, $oss_dir, $list['nextMarker'], $limit);
        return true;
    }

    /**
     * 获取本地保存目录
     * @param unknown $oss_type
     * @param unknown $oss_save_path
     */
    public function getLocalSavePath($oss_type, $oss_save_path) {
        switch ($oss_type) {
            case OSS_FILE_REPORT:
            case OSS_FILE_PAPER:
            case OSS_FILE_PAPER_MIJI:
            case OSS_FILE_PRI_TPL:
            case OSS_FILE_WXPAY_CERT:
            case OSS_FILE_WXCOUPON_PIC:
            case OSS_FILE_MP_ACTIVITY_IMG:
                $savepath = DATACENTER_ROOT.'/'.$oss_save_path;
                break;
            case OSS_FILE_REPORT_PARAGRAPH:
                break;
            case OSS_FILE_SITE_LOGO:
                setlocale(LC_ALL, 'zh_CN.UTF-8');
                $pathinfo = pathinfo($oss_save_path);
                $basename = $pathinfo['basename'];

                $savepath = DATACENTER_ROOT.'/assets/checksite/'.$basename;
                break;
            case OSS_FILE_SERVICE_ICO:
                setlocale(LC_ALL, 'zh_CN.UTF-8');
                $pathinfo = pathinfo($oss_save_path);
                $basename = $pathinfo['basename'];

                $savepath = DATACENTER_ROOT.'/assets/ico/'.$basename;
                break;
            default:
                $savepath = DATACENTER_ROOT.'/'.$oss_save_path;
                break;
        }
        $dirname = dirname($savepath);
        if(!is_dir($dirname)) {
            mkdir($dirname, 0755, true); // true代表层级创建
            chown($dirname, 'nobody');
            chgrp($dirname, 'nobody');
        }
        return $savepath;
    }


    /**
     * 获取oss保存目录
     * @param unknown $oss_type
     * @param unknown $localpath
     * @return string
     */
    public function getOssSavePath($oss_type, $localpath) {
        switch ($oss_type) {
            case OSS_FILE_REPORT:
            case OSS_FILE_PAPER:
            case OSS_FILE_PAPER_MIJI:
            case OSS_FILE_PRI_TPL:
            case OSS_FILE_WXPAY_CERT:
            case OSS_FILE_WXCOUPON_PIC:
            case OSS_FILE_MP_ACTIVITY_IMG:
                $savepath = trim(str_replace('datacenter', '', $localpath), '/');
                break;
            case OSS_FILE_REPORT_PARAGRAPH:
                break;
            case OSS_FILE_SITE_LOGO:
            case OSS_FILE_SERVICE_ICO:
                setlocale(LC_ALL, 'zh_CN.UTF-8');
                $pathinfo = pathinfo($localpath);
                $basename = $pathinfo['basename'];

                $savepath = $GLOBALS['ossfile2savedir'][$oss_type]."/".$basename;
                break;
            default:
                $savepath = trim(str_replace('datacenter', '', $localpath), '/');
                break;
        }
        return $savepath;
    }

    public function isExist($bucket, $object) {
        try{
            $ossClient = new OssClient(ALIYUN_OSS_ACCESS_KEY_ID, ALIYUN_OSS_ACCESS_KEY_SECRET, $this->endpoint);

            $exist = $ossClient->doesObjectExist($bucket, $object);
        } catch(OssException $e) {
            $this->setError($e->getMessage());
            return false;
        }
        return $exist;
    }

    public function getFileUrl($bucket, $object, $timeout=60) {
        try{
            $ossClient = new OssClient(ALIYUN_OSS_ACCESS_KEY_ID, ALIYUN_OSS_ACCESS_KEY_SECRET, $this->endpoint);

            $exist = $ossClient->signUrl($bucket, $object, $timeout);
        } catch(OssException $e) {
            $this->setError($e->getMessage());
            return false;
        }
        return $exist;
    }


# 下方为临时方法 用完即删
    /**
     * 列举文件列表
     * @param unknown $bucket       存储空间名称
     * @param unknown $oss_dir      oss存储目录 为空列举所有文件
     * @param unknown $marker       下一次查询标记(文件名)
     * @param number $limit         每次查询数量
     */
    public function getOssFileList2($bucket, $oss_dir=null, $marker=null, $limit=1) {
        $prefix = $oss_dir;
        $delimiter = '';
        $nextMarker = $marker;
        $maxkeys = $limit;

        $options = array(
            'prefix'    => $prefix,
            'delimiter' => $delimiter,
            'max-keys'  => $maxkeys,
            'marker'    => $nextMarker,
        );
        // prefix
        // 对Object名字进行分组的字符。
        // 所有Object名字包含指定的前缀,第一次出现Delimiter字符之间的Object作为一组元素(即CommonPrefixes)。

        // delimiter
        // 限定返回文件的Key必须以Prefix作为前缀。
        // 如果把Prefix设为某个文件夹名,就可以列举以此Prefix开头的文件,即该文件夹下递归的所有文件和子文件夹。
        // 在设置Prefix的基础上增加设置Delimiter为 / 时,返回值就只列举该文件夹下的文件,文件夹下的子文件夹名返回在CommonPrefixes中,子文件夹下递归的所有文件和文件夹不显示。
        // 例如,一个Bucket中存在三个Object : fun/test.jpg, fun/movie/001.avi,fun/movie/007.avi。若设定Prefix为 fun/,则返回三个Object;如果增加设定Delimiter为 /,则返回fun/test.jpg和fun/movie/ 。
        // 参数的长度必须小于1024字节。使用Prefix查询时,返回的Key中仍会包含Prefix。

        // marker
        // 设定从Marker之后按字母排序开始返回Object。
        // Marker用来实现分页显示效果,参数的长度必须小于1024字节。在做条件查询时,即使Marker在列表中不存在,也会从符合Marker字母排序的下一个开始打印。

        // max-keys
        // 指定返回Object的最大数。
        // 取值:大于0小于1000,默认值:100。
        // 如果因为Max-keys的设定无法一次完成列举,返回结果会附加一个<NextMarker>作为下一次列举的Marker。


        try {
            $ossClient = new OssClient('LTAI5tMJ1rbd6bmGxANvofqP', 'R26lxTP1TS5WpDgZnBpIcTgJWUsgCt', $this->endpoint);
            $listObjectInfo = $ossClient->listObjects($bucket, $options);
        } catch (OssException $e) {
            $this->setError($e->getMessage());
            return false;
        }

        $nlist['bucketName'] = $listObjectInfo->getBucketName();
        $nlist['prefix'] = $listObjectInfo->getPrefix();
        $nlist['marker'] = $listObjectInfo->getMarker();
        $nlist['nextMarker'] = $listObjectInfo->getNextMarker();
        $nlist['maxKeys'] = $listObjectInfo->getMaxKeys();
        $nlist['delimiter'] = $listObjectInfo->getDelimiter();
        $nlist['isTruncated'] = $listObjectInfo->getIsTruncated();
        $nlist['objectList'] = array();
        $nlist['prefixList'] = array();

        $objectList = $listObjectInfo->getObjectList(); // object list
        $prefixList = $listObjectInfo->getPrefixList(); // directory list
        if (!empty($objectList)) {
            foreach ($objectList as $objectInfo) {
                $nlist['objectList'][] = $objectInfo->getKey();
            }
        }
        if (!empty($prefixList)) {
            foreach ($prefixList as $prefixInfo) {
                $nlist['prefixList'][] = $prefixInfo->getPrefix();
            }
        }
        return $nlist;
    }

    /**
     * 下载文件到本地
     * @param unknown $bucket           存储空间名称
     * @param unknown $oss_save_path    oss下文件路径及名称
     * @param unknown $local_path       保存到本地路径
     */
    public function downloadToLocal2($bucket, $oss_save_path, $local_path) {
        $dir = dirname($local_path);
        if(!is_dir($dir)) {
            mkdir($dir, 0775, true);
            chown($dir, 'nobody');
            chgrp($dir, 'nobody');
        }

        $options = array(
            OssClient::OSS_FILE_DOWNLOAD => $local_path
        );

        try{
            $ossClient = new OssClient('LTAI5tMJ1rbd6bmGxANvofqP', 'R26lxTP1TS5WpDgZnBpIcTgJWUsgCt', $this->endpoint);
            $ossClient->getObject($bucket, $oss_save_path, $options);
        } catch(OssException $e) {
            $this->setError($e->getMessage());
            return false;
        }
        return true;
    }

    public function isExist2($bucket, $object) {
        try{
            $ossClient = new OssClient('LTAI5tMJ1rbd6bmGxANvofqP', 'R26lxTP1TS5WpDgZnBpIcTgJWUsgCt', $this->endpoint);

            $exist = $ossClient->doesObjectExist($bucket, $object);
        } catch(OssException $e) {
            $this->setError($e->getMessage());
            return false;
        }
        return $exist;
    }

    /**
     * 删除单个文件
     * @param unknown $bucket           存储空间名称
     * @param unknown $oss_save_path    oss下文件路径及名称 如abc/efg/123.jpg
     * @return boolean
     */
    public function delSingleFile2($bucket, $oss_save_path) {
        try{
            $ossClient = new OssClient('LTAI5tMJ1rbd6bmGxANvofqP', 'R26lxTP1TS5WpDgZnBpIcTgJWUsgCt', $this->endpoint);

            $ossClient->deleteObject($bucket, $oss_save_path);
        } catch(OssException $e) {
            $this->setError($e->getMessage());
            return false;
        }
        return true;
    }
}