在 Yii 2.0 中上传文件的接口,在多场景(每个场景的验证规则有差异)下复用的实现
1、上传文件的接口,之有仅实现了 上传选题素材 的场景,现在需要新增 上传基地图标 的场景,决定复用上传文件的接口,如图1
2、新增请求参数,scenario:可选,场景,default:默认;config_base_location_icon:基地设置的图标,默认:default
3、编辑 \api\rests\asset\UploadAction.php,场景通过构造初始化配置来设置
<?php /** * @link http://www.yiiframework.com/ * @copyright Copyright (c) 2008 Yii Software LLC * @license http://www.yiiframework.com/license/ */ namespace api\rests\asset; use Yii; use api\models\Upload; use api\services\AssetService; use yii\base\Exception; use yii\base\Model; use yii\web\UnprocessableEntityHttpException; use yii\web\UploadedFile; use yii\web\ServerErrorHttpException; /** * 上传资源:/assets/upload(asset/upload) * * 1、请求参数列表 * (1)files[]:必填,上传的文件 * (2)scenario:可选,场景,default:默认;config_base_location_icon:基地设置的图标,默认:default * * 2、输入数据验证规则 * (1)必填:files[] * (2)文件 * A、可接受上传的文件扩展名列表 * B、可接受上传的 MIME 类型列表 * C、上传文件所需最少多少 Byte 的大小 * D、上传文件所需最多多少 Byte 的大小 * E、给定属性最多能承载多少个文件 * (3)范围(['default', 'config_base_location_icon']):scenario * * 3、操作数据 * (1)上传文件至临时的资源目录 * * UploadAction implements the API endpoint for creating a new model from the given data. * * For more details and usage information on UploadAction, see the [guide article on rest controllers](guide:rest-controllers). * * @author Qiang Wang <shuijingwanwq@163.com> * @since 1.0 */ class UploadAction extends Action { /** * @var string the scenario to be assigned to the new model before it is validated and saved. */ public $scenario = Model::SCENARIO_DEFAULT; /** * @var string the name of the view action. This property is need to create the URL when the model is successfully created. */ public $viewAction = 'view'; /** * Uploads a new model. * @return array the model newly uploaded * @throws ServerErrorHttpException 如果创建目录失败,将抛出 500 HTTP 异常 * @throws ServerErrorHttpException if there is any error when creating the model * @throws Exception if the directory could not be created (i.e. php error due to parallel changes) */ public function run() { if ($this->checkAccess) { call_user_func($this->checkAccess, $this->id); } $request = Yii::$app->request; $scenario = $request->post('scenario', $this->scenario); /* 验证场景的范围(['default', 'config_base_location_icon']) */ if (!in_array($scenario, [$this->scenario, Upload::SCENARIO_CONFIG_BASE_LOCATION_ICON])) { throw new UnprocessableEntityHttpException(Yii::t('error', Yii::t('error', Yii::t('error', '226076'), ['scenario' => $scenario])), 226076); } /* @var $model Upload */ $model = new Upload(['scenario' => $scenario]); // 将 UploadedFile 实例数组赋值给 Upload::files $model->files = UploadedFile::getInstances($model, 'files'); if ($model->validate()) { $assetService = new AssetService(); $result = $assetService->uploadTempAssets($model->files); return ['code' => 10000, 'message' => Yii::t('success', '126033'), 'data' => ['items' => $result]]; } elseif ($model->hasErrors()) { $response = Yii::$app->getResponse(); $response->setStatusCode(422, 'Data Validation Failed.'); $firstError = ''; foreach ($model->getFirstErrors() as $message) { $firstError = $message; break; } return ['code' => 226004, 'message' => Yii::t('error', Yii::t('error', Yii::t('error', '226004'), ['first_error' => $firstError]))]; } elseif (!$model->hasErrors()) { throw new ServerErrorHttpException('Failed to upload the object for unknown reason.'); } } }
4、编辑 \common\logics\Upload.php,通过指定规则的 on 属性,一条规则可以只在某个 场景 下应用,以区分不同场景下的验证规则。例:上传选题素材为默认的场景,上传基地图标为基地设置的图标的场景。
<?php namespace common\logics; use Yii; use yii\base\Model; use yii\web\UploadedFile; class Upload extends Model { const SCENARIO_CONFIG_BASE_LOCATION_ICON = 'config_base_location_icon'; // 场景:基地设置的图标 /** * @var UploadedFile */ public $files; /** * {@inheritdoc} */ public function scenarios() { $scenarios = parent::scenarios(); return $scenarios; } public function rules() { return [ [['files'], 'image', 'skipOnEmpty' => false, 'extensions' => Yii::$app->params['pcsApi']['asset']['upload']['scenario']['configBaseLocationIcon']['extensions'], 'mimeTypes' => Yii::$app->params['pcsApi']['asset']['upload']['scenario']['configBaseLocationIcon']['mimeTypes'], 'minSize' => Yii::$app->params['pcsApi']['asset']['upload']['scenario']['configBaseLocationIcon']['minSize'], 'maxSize' => Yii::$app->params['pcsApi']['asset']['upload']['scenario']['configBaseLocationIcon']['maxSize'], 'maxFiles' => Yii::$app->params['pcsApi']['asset']['upload']['scenario']['configBaseLocationIcon']['maxFiles'], 'minWidth' => Yii::$app->params['pcsApi']['asset']['upload']['scenario']['configBaseLocationIcon']['minWidth'], 'maxWidth' => Yii::$app->params['pcsApi']['asset']['upload']['scenario']['configBaseLocationIcon']['maxWidth'], 'minHeight' => Yii::$app->params['pcsApi']['asset']['upload']['scenario']['configBaseLocationIcon']['minHeight'], 'maxHeight' => Yii::$app->params['pcsApi']['asset']['upload']['scenario']['configBaseLocationIcon']['maxHeight'], 'on' => self::SCENARIO_CONFIG_BASE_LOCATION_ICON], [['files'], 'file', 'skipOnEmpty' => false, 'extensions' => Yii::$app->params['pcsApi']['asset']['upload']['extensions'], 'mimeTypes' => Yii::$app->params['pcsApi']['asset']['upload']['mimeTypes'], 'minSize' => Yii::$app->params['pcsApi']['asset']['upload']['minSize'], 'maxSize' => Yii::$app->params['pcsApi']['asset']['upload']['maxSize'], 'maxFiles' => Yii::$app->params['pcsApi']['asset']['upload']['maxFiles']], ]; } public function formName() { return ''; } }
5、验证规则的具体参数配置在文件 \common\config\params-local.php
// 策划指挥系统接口 'pcsApi' => [ 'asset' => [ // 资源 'basePath' => 'E:/wwwroot/pcs-api/storage', // BASE PATH 'tempDir' => '/tmp', // TEMP DIR 'hostInfo' => 'http://127.0.0.1/pcs-api/storage', // HOME URL 'baseUrl' => '', // BASE URL 'upload' => [ // 上传 'extensions' => 'ogg, pdf, xml, zip, gz, mp4, mp3, wav, webm, gif, jpeg, jpg, png, webp, svg, svgz, tiff, css, csv, txt, vcf, vcard, mov, qt, mkv, mk3d, mka, mks, wmv, flv', // 可接受上传的文件扩展名列表 'mimeTypes' => 'application/ogg, application/pdf, application/xml, application/zip, application/gzip, audio/mp4, audio/mpeg, audio/ogg, audio/vnd.wave, audio/webm, image/gif, image/jpeg, image/png, image/webp, image/svg+xml, image/tiff, text/css, text/csv, text/plain, text/vcard, text/xml, video/mpeg, video/mp4, video/ogg, video/quicktime, video/webm, video/x-matroska, video/x-ms-wmv, video/x-flv', // 可接受上传的 MIME 类型列表 'minSize' => null, // 上传文件所需最少多少 Byte 的大小 'maxSize' => 1024 * 1024 * 1024, // 上传文件所需最多多少 Byte 的大小 'maxFiles' => 10, // 给定属性最多能承载多少个文件 'scenario' => [ // 场景 'configBaseLocationIcon' => [ // 基地设置的图标 'extensions' => 'jpeg, jpg, png', // 可接受上传的文件扩展名列表 'mimeTypes' => 'image/jpeg, image/png', // 可接受上传的 MIME 类型列表 'minSize' => null, // 上传文件所需最少多少 Byte 的大小 'maxSize' => 1024 * 1024 * 2, // 上传文件所需最多多少 Byte 的大小 'maxFiles' => 2, // 给定属性最多能承载多少个文件 'minWidth' => 50, // 图片的最小宽度 'maxWidth' => 200, // 图片的最大宽度 'minHeight' => 50, // 图片的最小高度 'maxHeight' => 200, // 图片的最大高度 ], ], ], ], ],
6、上传选题素材 的场景,文件扩展名为 zip 的文件,上传成功,符合预期,如图2
7、基地设置的图标 的场景,文件扩展名为 zip 的文件,上传失败,符合预期,如图3
8、maxFiles:给定属性最多能承载多少个文件。 默认为 1,代表只允许单文件上传。 若值大于一,那么输入值必须为包含最多 maxFiles 个上传文件元素的数组。基地设置的图标 的场景,上传文件的数量应该只能够为 1,现在 maxFiles:2,即可最多上传 2 个文件,原因在于 maxFiles:1 时,响应失败,如图4
9、在不同场景中,一些场景中文件数量只能够为 1 个,一些场景中文件数量可以为多个,现在 maxFiles:2,即可最多上传 2 个文件,如图5
10、编辑 \api\rests\asset\UploadAction.php,判断场景,如果为:config_base_location_icon,则上传单个文件(即第 1 个文件),现在 maxFiles:2,即可最多上传 2 个文件(2 个文件皆会验证,但仅会上传第 1 个文件),符合预期(其实此处最佳的方案应该是响应失败,提示仅能够上传一个文件的,但是却是需要再实现一个上传的接口,有所冗余,因此,最终决定,还是复用),如图6
<?php /** * @link http://www.yiiframework.com/ * @copyright Copyright (c) 2008 Yii Software LLC * @license http://www.yiiframework.com/license/ */ namespace api\rests\asset; use Yii; use api\models\Upload; use api\services\AssetService; use yii\base\Exception; use yii\base\Model; use yii\web\UnprocessableEntityHttpException; use yii\web\UploadedFile; use yii\web\ServerErrorHttpException; /** * 上传资源:/assets/upload(asset/upload) * * 1、请求参数列表 * (1)files[]:必填,上传的文件 * (2)scenario:可选,场景,default:默认;config_base_location_icon:基地设置的图标,默认:default * * 2、输入数据验证规则 * (1)必填:files[] * (2)文件 * A、可接受上传的文件扩展名列表 * B、可接受上传的 MIME 类型列表 * C、上传文件所需最少多少 Byte 的大小 * D、上传文件所需最多多少 Byte 的大小 * E、给定属性最多能承载多少个文件 * (3)范围(['default', 'config_base_location_icon']):scenario * * 3、操作数据 * (1)上传文件至临时的资源目录 * * UploadAction implements the API endpoint for creating a new model from the given data. * * For more details and usage information on UploadAction, see the [guide article on rest controllers](guide:rest-controllers). * * @author Qiang Wang <shuijingwanwq@163.com> * @since 1.0 */ class UploadAction extends Action { /** * @var string the scenario to be assigned to the new model before it is validated and saved. */ public $scenario = Model::SCENARIO_DEFAULT; /** * @var string the name of the view action. This property is need to create the URL when the model is successfully created. */ public $viewAction = 'view'; /** * Uploads a new model. * @return array the model newly uploaded * @throws ServerErrorHttpException 如果创建目录失败,将抛出 500 HTTP 异常 * @throws ServerErrorHttpException if there is any error when creating the model * @throws Exception if the directory could not be created (i.e. php error due to parallel changes) */ public function run() { if ($this->checkAccess) { call_user_func($this->checkAccess, $this->id); } $request = Yii::$app->request; $scenario = $request->post('scenario', $this->scenario); /* 验证场景的范围(['default', 'config_base_location_icon']) */ if (!in_array($scenario, [$this->scenario, Upload::SCENARIO_CONFIG_BASE_LOCATION_ICON])) { throw new UnprocessableEntityHttpException(Yii::t('error', Yii::t('error', Yii::t('error', '226076'), ['scenario' => $scenario])), 226076); } /* @var $model Upload */ $model = new Upload(['scenario' => $scenario]); // 将 UploadedFile 实例数组赋值给 Upload::files $model->files = UploadedFile::getInstances($model, 'files'); if ($model->validate()) { $assetService = new AssetService(); /* 判断场景,如果为:config_base_location_icon,则上传单个文件(即第 1 个文件) */ if ($scenario == Upload::SCENARIO_CONFIG_BASE_LOCATION_ICON) { // 将 UploadedFile 实例赋值给 Upload::files $model->files = [$model->files[0]]; } $result = $assetService->uploadTempAssets($model->files); return ['code' => 10000, 'message' => Yii::t('success', '126033'), 'data' => ['items' => $result]]; } elseif ($model->hasErrors()) { $response = Yii::$app->getResponse(); $response->setStatusCode(422, 'Data Validation Failed.'); $firstError = ''; foreach ($model->getFirstErrors() as $message) { $firstError = $message; break; } return ['code' => 226004, 'message' => Yii::t('error', Yii::t('error', Yii::t('error', '226004'), ['first_error' => $firstError]))]; } elseif (!$model->hasErrors()) { throw new ServerErrorHttpException('Failed to upload the object for unknown reason.'); } } }
近期评论