在 Yii 2.0 中上传文件的接口,在多场景(每个场景的验证规则有差异)下复用的实现
1、上传文件的接口,之有仅实现了 上传选题素材 的场景,现在需要新增 上传基地图标 的场景,决定复用上传文件的接口,如图1
2、新增请求参数,scenario:可选,场景,default:默认;config_base_location_icon:基地设置的图标,默认:default
3、编辑 \api\rests\asset\UploadAction.php,场景通过构造初始化配置来设置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 | <?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 属性,一条规则可以只在某个 场景 下应用,以区分不同场景下的验证规则。例:上传选题素材为默认的场景,上传基地图标为基地设置的图标的场景。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | <?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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | // 策划指挥系统接口 'pcsApi' => [ 'asset' => [ // 资源 'basePath' => 'E:/wwwroot/pcs-api/storage' , // BASE PATH 'tempDir' => '/tmp' , // TEMP DIR '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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 | <?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.' ); } } } |
近期评论