没有不值得去解决的问题,也没有不值得去学习的技术!

Implementing an Asset Service in Yii 2.0: Copy Source Asset Files to the Channel Publishing Directory and Return the Relative Path

After upgrading to the development environment (CentOS), the file copy succeeded

1. Configuration file: \common\config\params-local.php, self-media as the source

PHP

    // 自媒体
    'spider' => [
        'asset' => [ // 资源
            'image' => [ // 图片
                'hostInfo' => 'http://localhost/channel-pub-api/storage/spider', // HOME URL
                'baseUrl' => '/images', // BASE URL
                'basePath' => 'E:/wwwroot/channel-pub-api/storage/spider/images', // BASE PATH
            ],
            'video' => [ // 视频
                'hostInfo' => 'http://localhost/channel-pub-api/storage/spider', // HOME URL
                'baseUrl' => '/videos', // BASE URL
                'basePath' => 'E:/wwwroot/channel-pub-api/storage/spider/videos', // BASE PATH
            ],
        ],
    ],
    // 渠道发布接口
    'channelPubApi' => [
        'asset' => [ // 资源
            'image' => [ // 图片
                'basePath' => 'E:/wwwroot/channel-pub-api/storage/channel-pub-api/images', // BASE PATH
            ],
            'video' => [ // 视频
                'basePath' => 'E:/wwwroot/channel-pub-api/storage/channel-pub-api/videos', // BASE PATH
            ],
        ],
    ],

2. Create a new service class, \common\services\AssetService.php, and implement the static method: copyAssets

PHP
<?php
/**
 * Created by PhpStorm.
 * User: WangQiang
 * Date: 2018/09/20
 * Time: 14:04
 */
 
namespace common\services;
 
use Yii;
use yii\base\Model;
use common\logics\ChannelAppSource;
use yii\db\ActiveRecordInterface;
use yii\helpers\StringHelper;
use yii\helpers\FileHelper;
use yii\web\NotFoundHttpException;
use yii\web\UnprocessableEntityHttpException;
use yii\web\ServerErrorHttpException;
use yii\web\HttpException;
 
/**
 * 资源服务
 *
 * @author Qiang Wang <shuijingwanwq@163.com>
 * @since 1.0
 */
class AssetService extends Service
{
    /**
     * 复制来源的资源文件至渠道发布的资源目录,返回相对路径
     * @param string $source 来源
     * 格式如下:spider
     * @param array $assets 来源的资源文件的绝对URL
     * 格式如下:
     * [
     *     [
     *         'type' => 'image',
     *         'absolute_url' => 'http://localhost/channel-pub-api/storage/spider/images/1.png',
     *     ],
     *     [
     *         'type' => 'video',
     *         'absolute_url' => 'http://localhost/channel-pub-api/storage/spider/videos/7月份北上广深等十大城市租金环比上涨 看东方 20180820 高清_高清.mp4',
     *     ],
     * ]
     *
     * @return array $channelPubApiAssetAbsolutePaths 渠道发布的资源文件的相对路径
     * 格式如下:
     * [
     *     [
     *         'type' => 'image',
     *         'relative_path' => '/2018/09/20/1537439889.2333.1441541478.png',
     *     ],
     *     [
     *         'type' => 'video',
     *         'relative_path' => '/2018/09/20/1537439889.2403.62871268.mp4',
     *     ],
     * ]
     *
     * @throws UnprocessableEntityHttpException 如果来源的资源文件不是以来源的 HOME URL + BASE URL 开头,将抛出 422 HTTP 异常
     * @throws NotFoundHttpException 如果来源的资源文件不存在,将抛出 404 HTTP 异常
     * @throws ServerErrorHttpException 如果创建目录失败,将抛出 500 HTTP 异常
     * @throws ServerErrorHttpException 如果复制来源的资源文件至渠道发布的资源目录失败,将抛出 500 HTTP 异常
     * @throws \yii\base\Exception if the directory could not be created (i.e. php error due to parallel changes)
     */
    public static function copyAssets($source, $assets)
    {
        // 不是以来源的 HOME URL + BASE URL 开头的来源的资源文件
        $notAbsoluteUrlStartKeys = [];
        // 不存在的来源的资源文件
        $notExistsKeys = [];
        // 渠道发布的资源文件的相对路径
        $channelPubApiAssetAbsolutePaths = [];

        foreach ($assets as $key => $asset) {
            // 检查来源的资源文件的绝对URL是否以来源的 HOME URL + BASE URL 开头
            if (!StringHelper::startsWith($asset['absolute_url'], Yii::$app->params[$source]['asset'][$asset['type']]['hostInfo'] . Yii::$app->params[$source]['asset'][$asset['type']]['baseUrl'])) {
                $notAbsoluteUrlStartKeys[] = $key;
            } else {
                // 获取来源的资源文件的绝对路径
                $sourceAssetAbsolutePath = Yii::$app->params[$source]['asset'][$asset['type']]['basePath'] . str_replace(Yii::$app->params[$source]['asset'][$asset['type']]['hostInfo'] . Yii::$app->params[$source]['asset'][$asset['type']]['baseUrl'], '', $asset['absolute_url']);

                // 检查来源的资源文件是否存在
                if (!file_exists($sourceAssetAbsolutePath)) {
                    $notExistsKeys[] = $key;
                }
            }
        }

        if (!empty($notAbsoluteUrlStartKeys)) {
            $notAbsoluteUrlStartKeys = implode(",", $notAbsoluteUrlStartKeys);
            throw new UnprocessableEntityHttpException(Yii::t('common/error', Yii::t('common/error', Yii::t('common/error', '35003'), ['not_absolute_url_start_keys' => $notAbsoluteUrlStartKeys])), 35003);
        }

        if (!empty($notExistsKeys)) {
            $notExistsKeys = implode(",", $notExistsKeys);
            throw new NotFoundHttpException(Yii::t('common/error', Yii::t('common/error', Yii::t('common/error', '35004'), ['not_exists_keys' => $notExistsKeys])), 35004);
        }

        foreach ($assets as $key => $asset) {
            // 获取来源的资源文件的绝对路径
            $sourceAssetAbsolutePath = Yii::$app->params[$source]['asset'][$asset['type']]['basePath'] . str_replace(Yii::$app->params[$source]['asset'][$asset['type']]['hostInfo'] . Yii::$app->params[$source]['asset'][$asset['type']]['baseUrl'], '', $asset['absolute_url']);

            // 获取来源的资源文件的路径信息
            $pathInfo = pathinfo($sourceAssetAbsolutePath);
            // 渠道发布的资源文件的相对路径
            $directory = date('Y/m/d');
            $channelPubApiAssetRelativePath = '/' . $directory . '/' . microtime(true) . '.' . mt_rand() . '.' . $pathInfo['extension'];
            // 渠道发布的资源文件的绝对路径
            $channelPubApiAssetAbsolutePath = Yii::$app->params['channelPubApi']['asset'][$asset['type']]['basePath'] . $channelPubApiAssetRelativePath;
            // 创建目录
            if (!FileHelper::createDirectory(Yii::$app->params['channelPubApi']['asset'][$asset['type']]['basePath'] . '/' . $directory)) {
                throw new ServerErrorHttpException(Yii::t('common/error', Yii::t('common/error', Yii::t('common/error', '35005'), ['directory' => Yii::$app->params['channelPubApi']['asset'][$asset['type']]['basePath'] . '/' . $directory])), 35005);
            }
            // 复制来源的资源文件至渠道发布的资源目录
            if (!copy($sourceAssetAbsolutePath, $channelPubApiAssetAbsolutePath)) {
                throw new ServerErrorHttpException(Yii::t('common/error', Yii::t('common/error', Yii::t('common/error', '35006'), ['source_asset_absolute_path' => $sourceAssetAbsolutePath])), 35006);
            }

            $channelPubApiAssetAbsolutePaths[$key] = [
                'type' => $asset['type'],
                'relative_path' => $channelPubApiAssetRelativePath,
            ];
        }

        return $channelPubApiAssetAbsolutePaths;
    }
}

3. Reference the service class, \common\services\AssetService.php, and call the static method: copyAssets

PHP

        $assets = [
            [
                'type' => 'image',
                'absolute_url' => 'http://localhost/channel-pub-api/storage/spider/images/1.png',
            ],
            [
                'type' => 'video',
                'absolute_url' => 'http://localhost/channel-pub-api/storage/spider/videos/7月份北上广深等十大城市租金环比上涨 看东方 20180820 高清_高清.mp4',
            ],
        ];
        $assetServiceResult = AssetService::copyAssets('spider', $assets);
        print_r($assetServiceResult);
        exit;

4. After running the method, print out $assetServiceResult

Plaintext

Array
(
    [0] => Array
        (
            [type] => image
            [relative_path] => /2018/09/21/1537495742.6172.473015079.png
        )

    [1] => Array
        (
            [type] => video
            [relative_path] => /2018/09/21/1537495742.6251.651458618.mp4
        )

)


5. Check the copied directory and files; they match expectations, as shown in Figure 1

Check the copied directory and files; they match expectations
Figure 1

6. After upgrading to the development environment (CentOS), the file copy succeeded, as shown in Figure 2

After upgrading to the development environment (CentOS), the file copy succeeded
Figure 2

关于作者 · 持续学习与技术实践

我是一名拥有 15 年以上开发经验的技术从业者,自 2013 年起持续运营个人技术博客,记录工作与个人项目中遇到的真实问题,以及解决问题过程中的探索、实践和思考。

除了技术写作,我也在持续开发和维护自己的独立项目,探索新技术和新工具在实际场景中的应用。我始终相信,没有不值得去解决的问题,也没有不值得去学习的技术。

如果这篇文章对你有所帮助,欢迎浏览博客中的其他文章,也欢迎交流技术经验与想法。

关于我  ·  GitHub  ·  邮件联系