1. The file upload API previously only supported the “upload topic material” scenario. Now I need to add an “upload base icon” scenario, so I decided to reuse the file upload API, as shown in Figure 1.

2. Add a new request parameter, scenario: optional, the scenario, default: default; config_base_location_icon: the icon for base settings, default: default.
3. Edit \api\rests\asset\UploadAction.php. The scenario is set through constructor initialization configuration.
<?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. Edit \common\logics\Upload.php. By specifying the on property of a rule, a rule can be applied only in a specific scenario to differentiate validation rules across scenarios. For example: uploading topic material is the default scenario, while uploading a base icon is the base settings icon scenario.
<?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. The specific parameter configuration for the validation rules is in the file \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. In the “upload topic material” scenario, a file with the zip extension uploads successfully, as expected, as shown in Figure 2.

7. In the “base settings icon” scenario, a file with the zip extension fails to upload, as expected, as shown in Figure 3.

8. maxFiles: The maximum number of files a given attribute can hold. It defaults to 1, meaning only single-file uploads are allowed. If the value is greater than one, the input value must be an array containing at most maxFiles uploaded file elements. In the “base settings icon” scenario, the number of uploaded files should only be 1. Currently, maxFiles is set to 2, allowing up to 2 files to be uploaded. The reason for this is that when maxFiles is 1, the response fails, as shown in Figure 4.

9. In different scenarios, some scenarios allow only 1 file, while others allow multiple files. Currently, maxFiles is set to 2, allowing up to 2 files to be uploaded, as shown in Figure 5.

10. Edit \api\rests\asset\UploadAction.php to check the scenario. If it is config_base_location_icon, upload a single file (i.e., the first file). Currently, maxFiles is set to 2, allowing up to 2 files to be uploaded (both files will be validated, but only the first file will be uploaded), as expected. Actually, the best approach here would be to respond with a failure and prompt that only one file can be uploaded, but that would require implementing another upload API, which is redundant. Therefore, I ultimately decided to reuse the existing one, as shown in Figure 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.');
}
}
}
