fangliang
9 years ago
8 changed files with 488 additions and 48 deletions
@ -0,0 +1,204 @@
|
||||
文件上传 |
||||
============ |
||||
|
||||
在Yii里上传文件通常使用[[yii\web\UploadedFile]]类, |
||||
它把每个上传的文件封装成 `UploadedFile` 对象。 |
||||
结合[[yii\widgets\ActiveForm]]和[models](structure-models.md),你可以轻松实现安全的上传文件机制。 |
||||
|
||||
|
||||
##创建模型 <span id="creating-models"></span> |
||||
|
||||
和普通的文本输入框类似,当要上传一个文件时,你需要创建一个模型类并且用其中的某个属性来接收上传的文件实例。 |
||||
你还需要声明一条验证规则以验证上传的文件。 |
||||
举例来讲, |
||||
|
||||
```php |
||||
namespace app\models; |
||||
|
||||
use yii\base\Model; |
||||
use yii\web\UploadedFile; |
||||
|
||||
class UploadForm extends Model |
||||
{ |
||||
/** |
||||
* @var UploadedFile |
||||
*/ |
||||
public $imageFile; |
||||
|
||||
public function rules() |
||||
{ |
||||
return [ |
||||
[['imageFile'], 'file', 'skipOnEmpty' => false, 'extensions' => 'png, jpg'], |
||||
]; |
||||
} |
||||
|
||||
public function upload() |
||||
{ |
||||
if ($this->validate()) { |
||||
$this->imageFile->saveAs('uploads/' . $this->imageFile->baseName . '.' . $this->imageFile->extension); |
||||
return true; |
||||
} else { |
||||
return false; |
||||
} |
||||
} |
||||
} |
||||
``` |
||||
|
||||
在以上代码里,`imageFile` 属性用于接收上传的文件实例。它对应一条`file` 验证规则, |
||||
该规则使用 [[yii\validators\FileValidator]] 来确保只上传扩展名为 `png` 或 `jpg` 的文件。 |
||||
`upload()` 方法会执行该验证并且把上传的文件保存在服务器上。 |
||||
|
||||
通过 `file` 验证器,你可以检查文件的扩展名,大小,MIME类型等等。详情请查阅 |
||||
[Core Validatators](tutorial-core-validators.md#file) 章节。 |
||||
|
||||
>提示: 如果你要上传的是一张图片,可以考虑使用`image`验证器。 |
||||
`image` 验证器是通过[[yii\validators\ImageValidator]]实现验证的,确保对应的模型属性 |
||||
收到的文件是有效的图片文件,然后才保存,或者使用扩展类[Imagine Extension](https://github.com/yiisoft/yii2-imagine)进行处理. |
||||
|
||||
|
||||
##渲染文件输入 <span id="rendering-file-input"></span> |
||||
|
||||
接下来,在视图里创建一个文件输入控件 |
||||
|
||||
```php |
||||
<?php |
||||
use yii\widgets\ActiveForm; |
||||
?> |
||||
|
||||
<?php $form = ActiveForm::begin(['options' => ['enctype' => 'multipart/form-data']]) ?> |
||||
|
||||
<?= $form->field($model, 'imageFile')->fileInput() ?> |
||||
|
||||
<button>Submit</button> |
||||
|
||||
<?php ActiveForm::end() ?> |
||||
``` |
||||
|
||||
需要注意的是要记得在表单选项里加入 `enctype` 属性以确保文件能被正常上传。 |
||||
`fileInput()` 方法会渲染一个 `<input type="file">` 标签,让用户可以选择一个文件上传。 |
||||
|
||||
|
||||
## 视图和模型的连接 <span id="wiring-up"></span> |
||||
|
||||
现在,在控制器方法里编写连接模型和视图的代码以实现文件上传。 |
||||
|
||||
```php |
||||
namespace app\controllers; |
||||
|
||||
use Yii; |
||||
use yii\web\Controller; |
||||
use app\models\UploadForm; |
||||
use yii\web\UploadedFile; |
||||
|
||||
class SiteController extends Controller |
||||
{ |
||||
public function actionUpload() |
||||
{ |
||||
$model = new UploadForm(); |
||||
|
||||
if (Yii::$app->request->isPost) { |
||||
$model->imageFile = UploadedFile::getInstance($model, 'imageFile'); |
||||
if ($model->upload()) { |
||||
// 文件上传成功 |
||||
return; |
||||
} |
||||
} |
||||
|
||||
return $this->render('upload', ['model' => $model]); |
||||
} |
||||
} |
||||
``` |
||||
|
||||
在上面的代码里,当提交表单的时候,[[yii\web\UploadedFile::getInstance()]]方法就被调用, |
||||
上传的文件用一个 `UploadedFile` 实例表示。然后,我们依靠模型的验证规则确保上传的文件是有效的, |
||||
并将文件保存在服务器上。 |
||||
|
||||
|
||||
## 上传多个文件 <span id="uploading-multiple-files"></span> |
||||
|
||||
将前面所述的代码做一些调整,也可以一次性上传多个文件。 |
||||
|
||||
首先你得调整模型类,在 `file` 验证规则里增加一个 `maxFiles` 选项,用以限制一次上传文件的最大数量。 |
||||
`upload()`方法也得修改, |
||||
以便一个一个地保存上传的文件。 |
||||
|
||||
```php |
||||
namespace app\models; |
||||
|
||||
use yii\base\Model; |
||||
use yii\web\UploadedFile; |
||||
|
||||
class UploadForm extends Model |
||||
{ |
||||
/** |
||||
* @var UploadedFile[] |
||||
*/ |
||||
public $imageFiles; |
||||
|
||||
public function rules() |
||||
{ |
||||
return [ |
||||
[['imageFiles'], 'file', 'skipOnEmpty' => false, 'extensions' => 'png, jpg', 'maxFiles' => 4], |
||||
]; |
||||
} |
||||
|
||||
public function upload() |
||||
{ |
||||
if ($this->validate()) { |
||||
foreach ($this->imageFiles as $file) { |
||||
$file->saveAs('uploads/' . $file->baseName . '.' . $file->extension); |
||||
} |
||||
return true; |
||||
} else { |
||||
return false; |
||||
} |
||||
} |
||||
} |
||||
``` |
||||
|
||||
在视图文件里,你需要把 `multiple` 选项添加到`fileInput()`函数调用里, |
||||
这样文件输入控件就可以接收多个文件。 |
||||
|
||||
```php |
||||
<?php |
||||
use yii\widgets\ActiveForm; |
||||
?> |
||||
|
||||
<?php $form = ActiveForm::begin(['options' => ['enctype' => 'multipart/form-data']]) ?> |
||||
|
||||
<?= $form->field($model, 'imageFiles[]')->fileInput(['multiple' => true, 'accept' => 'image/*']) ?> |
||||
|
||||
<button>Submit</button> |
||||
|
||||
<?php ActiveForm::end() ?> |
||||
``` |
||||
|
||||
最后,在控制器的 action 方法中,你应该调用 `UploadedFile::getInstances()` 而不是 `UploadedFile::getInstance()` 来把 |
||||
`UploadedFile` 实例数组赋值给 `UploadForm::imageFiles`。 |
||||
|
||||
```php |
||||
namespace app\controllers; |
||||
|
||||
use Yii; |
||||
use yii\web\Controller; |
||||
use app\models\UploadForm; |
||||
use yii\web\UploadedFile; |
||||
|
||||
class SiteController extends Controller |
||||
{ |
||||
public function actionUpload() |
||||
{ |
||||
$model = new UploadForm(); |
||||
|
||||
if (Yii::$app->request->isPost) { |
||||
$model->imageFiles = UploadedFile::getInstances($model, 'imageFiles'); |
||||
if ($model->upload()) { |
||||
// 文件上传成功 |
||||
return; |
||||
} |
||||
} |
||||
|
||||
return $this->render('upload', ['model' => $model]); |
||||
} |
||||
} |
||||
``` |
@ -0,0 +1,215 @@
|
||||
认证 |
||||
============== |
||||
|
||||
认证是鉴定用户身份的过程。它通常使用一个标识符 |
||||
(如用户名或电子邮件地址)和一个加密令牌(比如密码或者存取令牌)来 |
||||
鉴别用户身份。认证是登录功能的基础。 |
||||
|
||||
Yii提供了一个认证框架,它连接了不同的组件以支持登录。欲使用这个框架, |
||||
你主要需要做以下工作: |
||||
|
||||
* 设置用户组件 [[yii\web\User|user]] ; |
||||
* 创建一个类实现 [[yii\web\IdentityInterface]] 接口。 |
||||
|
||||
|
||||
## 配置 [[yii\web\User]] <span id="configuring-user"></span> |
||||
|
||||
用户组件 [[yii\web\User|user]] 用来管理用户的认证状态。这需要你 |
||||
指定一个含有实际认证逻辑的认证类 [[yii\web\User::identityClass|identity class]]。 |
||||
在以下web应用的配置项中,将用户用户组件 [[yii\web\User|user]] 的 |
||||
认证类 [[yii\web\User::identityClass|identity class]] 配置成 |
||||
模型类 `app\models\User`, 它的实现将在下一节中讲述。 |
||||
|
||||
```php |
||||
return [ |
||||
'components' => [ |
||||
'user' => [ |
||||
'identityClass' => 'app\models\User', |
||||
], |
||||
], |
||||
]; |
||||
``` |
||||
|
||||
|
||||
## 认证接口 [[yii\web\IdentityInterface]] 的实现 <span id="implementing-identity"></span> |
||||
|
||||
认证类 [[yii\web\User::identityClass|identity class]] 必须实现包含以下方法的 |
||||
认证接口 [[yii\web\IdentityInterface]]: |
||||
|
||||
* [[yii\web\IdentityInterface::findIdentity()|findIdentity()]]:根据指定的用户ID查找 |
||||
认证模型类的实例,当你需要使用session来维持登录状态的时候会用到这个方法。 |
||||
* [[yii\web\IdentityInterface::findIdentityByAccessToken()|findIdentityByAccessToken()]]:根据指定的存取令牌查找 |
||||
认证模型类的实例,该方法用于 |
||||
通过单个加密令牌认证用户的时候(比如无状态的RESTful应用)。 |
||||
* [[yii\web\IdentityInterface::getId()|getId()]]:获取该认证实例表示的用户的ID。 |
||||
* [[yii\web\IdentityInterface::getAuthKey()|getAuthKey()]]:获取基于 cookie 登录时使用的认证密钥。 |
||||
认证密钥储存在 cookie 里并且将来会与服务端的版本进行比较以确保 |
||||
cookie的有效性。 |
||||
* [[yii\web\IdentityInterface::validateAuthKey()|validateAuthKey()]] :是基于 cookie 登录密钥的 |
||||
验证的逻辑的实现。 |
||||
|
||||
用不到的方法可以空着,例如,你的项目只是一个 |
||||
无状态的 RESTful 应用,只需实现 [[yii\web\IdentityInterface::findIdentityByAccessToken()|findIdentityByAccessToken()]] |
||||
和 [[yii\web\IdentityInterface::getId()|getId()]] ,其他的方法的函数体留空即可。 |
||||
|
||||
下面的例子是一个通过结合了 `user` 数据表的 |
||||
AR 模型 [Active Record](db-active-record.md) 实现的一个认证类 [[yii\web\User::identityClass|identity class]]。 |
||||
|
||||
```php |
||||
<?php |
||||
|
||||
use yii\db\ActiveRecord; |
||||
use yii\web\IdentityInterface; |
||||
|
||||
class User extends ActiveRecord implements IdentityInterface |
||||
{ |
||||
public static function tableName() |
||||
{ |
||||
return 'user'; |
||||
} |
||||
|
||||
/** |
||||
* 根据给到的ID查询身份。 |
||||
* |
||||
* @param string|integer $id 被查询的ID |
||||
* @return IdentityInterface|null 通过ID匹配到的身份对象 |
||||
*/ |
||||
public static function findIdentity($id) |
||||
{ |
||||
return static::findOne($id); |
||||
} |
||||
|
||||
/** |
||||
* 根据 token 查询身份。 |
||||
* |
||||
* @param string $token 被查询的 token |
||||
* @return IdentityInterface|null 通过 token 得到的身份对象 |
||||
*/ |
||||
public static function findIdentityByAccessToken($token, $type = null) |
||||
{ |
||||
return static::findOne(['access_token' => $token]); |
||||
} |
||||
|
||||
/** |
||||
* @return int|string 当前用户ID |
||||
*/ |
||||
public function getId() |
||||
{ |
||||
return $this->id; |
||||
} |
||||
|
||||
/** |
||||
* @return string 当前用户的(cookie)认证密钥 |
||||
*/ |
||||
public function getAuthKey() |
||||
{ |
||||
return $this->auth_key; |
||||
} |
||||
|
||||
/** |
||||
* @param string $authKey |
||||
* @return boolean if auth key is valid for current user |
||||
*/ |
||||
public function validateAuthKey($authKey) |
||||
{ |
||||
return $this->getAuthKey() === $authKey; |
||||
} |
||||
} |
||||
``` |
||||
|
||||
如上所述,如果你的应用利用 cookie 登录, |
||||
你只需要实现 `getAuthKey()` 和 `validateAuthKey()` 方法。这样的话,你可以使用下面的代码在 |
||||
`user` 表中生成和存储每个用户的认证密钥。 |
||||
|
||||
```php |
||||
class User extends ActiveRecord implements IdentityInterface |
||||
{ |
||||
...... |
||||
|
||||
public function beforeSave($insert) |
||||
{ |
||||
if (parent::beforeSave($insert)) { |
||||
if ($this->isNewRecord) { |
||||
$this->auth_key = \Yii::$app->security->generateRandomString(); |
||||
} |
||||
return true; |
||||
} |
||||
return false; |
||||
} |
||||
} |
||||
``` |
||||
|
||||
> 注意:不要混淆 `user` 认证类和用户组件 [[yii\web\User]]。前者是实现 |
||||
认证逻辑的类,通常用关联了 |
||||
持久性存储的用户信息的AR模型 [Active Record](db-active-record.md) 实现。后者是负责管理用户认证状态的 |
||||
应用组件。 |
||||
|
||||
|
||||
## 使用用户组件 [[yii\web\User]] <span id="using-user"></span> |
||||
|
||||
在 `user` 应用组件方面,你主要用到 [[yii\web\User]] 。 |
||||
|
||||
你可以使用表达式 `Yii::$app->user->identity` 检测当前用户身份。它返回 |
||||
一个表示当前登录用户的认证类 [[yii\web\User::identityClass|identity class]] 的实例, |
||||
未认证用户(游客)则返回 null。下面的代码展示了如何从 [[yii\web\User]] |
||||
获取其他认证相关信息: |
||||
|
||||
```php |
||||
// 当前用户的身份实例。未认证用户则为 Null 。 |
||||
$identity = Yii::$app->user->identity; |
||||
|
||||
// 当前用户的ID。 未认证用户则为 Null 。 |
||||
$id = Yii::$app->user->id; |
||||
|
||||
// 判断当前用户是否是游客(未认证的) |
||||
$isGuest = Yii::$app->user->isGuest; |
||||
``` |
||||
|
||||
你可以使用下面的代码登录用户: |
||||
|
||||
```php |
||||
// 使用指定用户名获取用户身份实例。 |
||||
// 请注意,如果需要的话您可能要检验密码 |
||||
$identity = User::findOne(['username' => $username]); |
||||
|
||||
// 登录用户 |
||||
Yii::$app->user->login($identity); |
||||
``` |
||||
|
||||
[[yii\web\User::login()]] 方法将当前用户的身份登记到 [[yii\web\User]]。如果 session 设置为 |
||||
[[yii\web\User::enableSession|enabled]],则使用 session 记录用户身份,用户的 |
||||
认证状态将在整个会话中得以维持。如果开启自动登录 [[yii\web\User::enableAutoLogin|enabled]] |
||||
则基于 cookie 登录(如:记住登录状态),它将使用 cookie 保存用户身份,这样 |
||||
只要 cookie 有效就可以恢复登录状态。 |
||||
|
||||
为了使用 cookie 登录,你需要在应用配置文件中将 [[yii\web\User::enableAutoLogin]] |
||||
设为 true。你还需要在 [[yii\web\User::login()]] 方法中 |
||||
传递有效期(记住登录状态的时长)参数。 |
||||
|
||||
注销用户: |
||||
|
||||
```php |
||||
Yii::$app->user->logout(); |
||||
``` |
||||
|
||||
请注意,启用 session 时注销用户才有意义。该方法将从内存和 session 中 |
||||
同时清理用户认证状态。默认情况下,它还会注销所有的 |
||||
用户会话数据。如果你希望保留这些会话数据,可以换成 `Yii::$app->user->logout(false)` 。 |
||||
|
||||
|
||||
## 认证事件 <span id="auth-events"></span> |
||||
|
||||
[[yii\web\User]] 类在登录和注销流程引发一些事件。 |
||||
|
||||
* [[yii\web\User::EVENT_BEFORE_LOGIN|EVENT_BEFORE_LOGIN]]:在登录 [[yii\web\User::login()]] 时引发。 |
||||
如果事件句柄将事件对象的 [[yii\web\UserEvent::isValid|isValid]] 属性设为 false, |
||||
登录流程将会被取消。 |
||||
* [[yii\web\User::EVENT_AFTER_LOGIN|EVENT_AFTER_LOGIN]]:登录成功后引发。 |
||||
* [[yii\web\User::EVENT_BEFORE_LOGOUT|EVENT_BEFORE_LOGOUT]]:注销 [[yii\web\User::logout()]] 前引发。 |
||||
如果事件句柄将事件对象的 [[yii\web\UserEvent::isValid|isValid]] 属性设为 false, |
||||
注销流程将会被取消。 |
||||
* [[yii\web\User::EVENT_AFTER_LOGOUT|EVENT_AFTER_LOGOUT]]:成功注销后引发。 |
||||
|
||||
你可以通过响应这些事件来实现一些类似登录统计、在线人数统计的功能。例如, |
||||
在登录后 [[yii\web\User::EVENT_AFTER_LOGIN|EVENT_AFTER_LOGIN]] 的处理程序,你可以将用户的登录时间和IP记录到 |
||||
`user` 表中。 |
Loading…
Reference in new issue