hyperf 上傳檔案至華為雲 obs

ezreal_rao發表於2019-12-15

前提

華為的obs上傳sdk寫的有點一言難盡,而且包的版本全部指定了,無法在hyperf框架中使用。所以自己寫了一個。
直接上程式碼:

<?php
/**
 * Created by EzrealRao MacBook
 * User: Ezreal Rao
 * Date: 2019/12/8
 * Time: 20:49
 */

namespace App\Services;

use GuzzleHttp\Client;
use Hyperf\HttpMessage\Upload\UploadedFile;

class HuaWeiService
{
    /**
     * @var array
     */
    private $config;

    /**
     * 物件策略
     * @var string
     */
    private $obsAcl = 'public-read';

    /**
     * @var UploadedFile
     */
    private $file;

    /**
     * 過期時間
     * @var int
     */
    private $expires = 3600;

    /**
     * @var int
     */
    private $boundary = 9431149156168;

    /**
     * @var string
     */
    protected $logName = 'ObsUpload';

    /**
     * HuaWeiService constructor.
     */
    public function __construct()
    {
        $this->config = config('hw');
    }

    /**
     * @param UploadedFile $file
     * @param $filePath string 檔案路徑
     * @return array
     */
    public function post(UploadedFile $file, $filePath = 'images')
    {
        $this->file = $file;
        //獲取字尾
        $fileExtension = $file->getExtension();
        //生成檔名
        $fileName = trim($filePath, '/') . '/' . date('YmdHis') . '_' . uniqid() . '.' . $fileExtension;
        $buffers = $this->getBuffers($fileName);
        $buffer = $buffers['buffers'];
        $contentLength = $buffers['contentLength'];

        $requestBody = [
            'headers' => [
                'Content-Type' => 'multipart/form-data; boundary=' . $this->boundary,
                'Content-Length' =>strval($contentLength) ,
            ],
            'body' => implode('', $buffer),
        ];
        $scheme = $this->config['obs_scheme'];
        $port = $scheme == 'https' ? '443' : '80';
        $host = $scheme . '://' . $this->config['bucket'] . '.' . $this->config['endpoint'] . ':' . $port;
        logger($this->logName)->info('begin request', [
            'requestBody' => $requestBody,
            'host' => $host
        ]);
        $client = new Client([
            'verify' => false
        ]);
        try {
            $request = $client->request('POST', $host, $requestBody);
            $response = $request->getBody()->getContents();
            logger($this->logName)->info('result success', [$response]);
            return [
                'status' => true,
                'file_path' => $fileName,
                'msg' => null
            ];
        } catch (\Exception $e) {
            $message = $e->getResponse()->getBody()->getContents();
            $message = json_decode(json_encode(simplexml_load_string($message, 'SimpleXMLElement', LIBXML_NOCDATA)), true);
            logger($this->logName)->error('result failed', ['message' => $message]);
            return [
                'status' => false,
                'file_path' => null,
                'msg' => $message
            ];
        }
    }

    /**
     * 獲取策略
     * @param $fileName
     * @return false|string
     */
    private function getPolicy($fileName)
    {
        //頭部過期時間
        $expires = gmdate('Y-m-d\TH:i:s\Z', time() + $this->expires);

        $policy = [
            'expiration' => $expires,
            'conditions' => [
                ['bucket' => $this->config['bucket']],
                ['key' => $fileName],
                ['x-obs-acl' => $this->obsAcl],
                ['content-type' => $this->file->getClientMediaType()],
            ]
        ];
        logger($this->logName)->info('policy', $policy);
        return json_encode($policy);
    }

    /**
     * 獲取簽名
     * @param $originPolicy
     * @return string
     */
    private function getSign($originPolicy)
    {
        $policy = base64_encode($originPolicy);
        $sign =  base64_encode(hash_hmac('sha1', $policy, $this->config['secret'], true));
        logger($this->logName)->info('sign', [$sign]);
        return $sign;
    }

    /**
     * 返回表單引數
     * @param $fileName
     * @return array
     */
    private function getFormParams($fileName)
    {
        $policy = $this->getPolicy($fileName);
        $sign = $this->getSign($policy);
        $formParams = [
            'x-obs-acl' => $this->obsAcl,
            'content-type' => $this->file->getClientMediaType(),
            'key' => $fileName,
            'policy' => base64_encode($policy),
            'Accesskeyid' => $this->config['key'],
            'signature' => $sign
        ];
        logger($this->logName)->info('formParams', $formParams);
        return $formParams;
    }

    /**
     * 獲取buffers
     * @param $fileName
     * @return array
     */
    private function getBuffers($fileName)
    {
        $formParams = $this->getFormParams($fileName);
        $buffers = [];
        $contentLength = 0;

        /**
         * 從formParams 構造
         */
        $buffer = [];
        $first = true;
        foreach ($formParams as $key => $val){
            if(!$first){
                $buffer[] = "\r\n";
            }else{
                $first = false;
            }

            $buffer[] = "--";
            $buffer[] = $this->boundary;
            $buffer[] = "\r\n";
            $buffer[] = "Content-Disposition: form-data; name=\"";
            $buffer[] = strval($key);
            $buffer[] = "\"\r\n\r\n";
            $buffer[] = strval($val);
        }

        $buffer = implode('', $buffer);
        $contentLength += strlen($buffer);
        $buffers[] = $buffer;

        /**
         * 構造描述
         */
        $buffer = [];

        $buffer[] = "\r\n";
        $buffer[] = "--";
        $buffer[] = $this->boundary;
        $buffer[] = "\r\n";
        $buffer[] = "Content-Disposition: form-data; name=\"file\"; filename=\"";
        $buffer[] = $fileName;
        $buffer[] = "\"\r\n";
        $buffer[] = "Content-Type: " . $this->file->getClientMediaType();
        $buffer[] = "\r\n\r\n";

        $buffer = implode('', $buffer);
        $contentLength += strlen($buffer);
        $buffers[] = $buffer;

        /**
         * 構造檔案資料
         */
        $buffer = [];

        $fp = fopen($this->file->getRealPath(), 'r');
        if($fp){
            while(!feof($fp)){
                $buffer[] = fgetc($fp);
            }
            fclose($fp);
        }

        $buffer = implode('', $buffer);
        $contentLength += strlen($buffer);
        $buffers[] = $buffer;

        /**
         * 結束構造
         */
        $buffer = [];
        $buffer[] = "\r\n--";
        $buffer[] = $this->boundary;
        $buffer[] = "--\r\n";

        $buffer = implode('', $buffer);
        $contentLength += strlen($buffer);
        $buffers[] = $buffer;
        $data =  [
            'buffers' => $buffers,
            'contentLength' => $contentLength
        ];
        return $data;
    }

}

新增配置檔案config/autoload/hw.php

return [
    'key' => env('OBS_ACCESS_KEY', ''),
    'secret' => env('OBS_ACCESS_SECRET', ''),
    'endpoint' => env('OBS_ENDPOINT', ''),
    'bucket' => env('OBS_BUCKET', ''),
    'obs_cdn_endpoint' => env('OBS_CDN_ENDPOINT', ''),
    'obs_scheme' => env('OBS_SCHEME', 'https')
];

完成!

本作品採用《CC 協議》,轉載必須註明作者和本文連結

相關文章