同福

做个用户管理系统(26)——设置用户头像【20201214】

介绍

介绍

福哥今天会带着大家完成设置用户头像的功能的开发。设置用户头像是一个特殊的表单,它含有文件上传功能的HTML输入控件,处理这种表单需要一些技巧。

文件上传功能需要使用类型为file的HTML输入控件,我们的TFPHP框架的表单是使用AJAX技术进行提交的,而前面我们使用jQuery的serialize方法收集表单数据的方式并不适用与含有文件上传控件的情况,所以我们的form控件也需要改造一下。

前面福哥已经带着大家完成了图片加工的对象TFImage的开发,有了TFImage我们就可以方便地制作用户头像图片。这里有一点需要注意的是,制作好的新头像图片文件名需要存储到user_profile表里,那么如果不是第一次设置头像,则除了保存新头像图片文件名之外还需要根据旧头像图片文件名删除已有的头像图片文件,如若不然,会产生一堆“失联”的无主头像图片文件了。

模型user

setIcon

public function setIcon(int $userID, array $file):int {
    $tfdo = $this->tfphp->getDatabase()->getTFDO();

    // test user
    $userInfo = $this->getByTable("user", array($userID));
    if($userInfo == null){

        return 1;
    }

    // upsert user profile
    $profileInfo = $this->getByTable("user_profile", array($userID));
    if($profileInfo == null){
        $ret = $tfdo->insert("user_profile", array(
            'userID'=>$userID,
        ), array(
            'userID'=>"int",
        ));
        if(!$ret){

            return 2;
        }
    }

    // make new image file name
    $oldFileName = $profileInfo['iconFileName'];
    $newFileName = md5(microtime());
    switch($file['type']){
        case "image/jpeg":
            $newFileName .= ".jpg";
            break;
        case "image/png":
            $newFileName .= ".png";
            break;
        case "image/gif":
            $newFileName .= ".gif";
            break;
        default:
            $newFileName .= ".img";
            break;
    }
    $oldFilePath = WEB_ROOT_PATH. "userfiles/icon/". $oldFileName;
    $newFilePath = WEB_ROOT_PATH. "userfiles/icon/". $newFileName;

    // delete old image
    if($oldFileName != ""){
        if(file_exists($oldFilePath)) {
            unlink($oldFilePath);
        }
        if(file_exists($oldFilePath)){

            return 3;
        }
    }

    // center drop image
    try{
        $image = new TFImage($this->tfphp);

        $image->createFromFile($file['tmp_name']);
        $centerDropImage = $image->centerDrop(192, 192);
        $centerDropImage->save($newFilePath);
    }
    catch(\Exception $e){

        return 3;
    }
    if(!file_exists($newFilePath)){

        return 3;
    }

    // update user profile
    $ret = $tfdo->update("user_profile", array(
        'iconFileName'=>$newFileName,
    ), null, "userID = @int", array($userID));
    if(!$ret){

        return 2;
    }

    return 0;
}

接口控制器

doSave

protected function doSave(){
    $req = $this->tfphp->getRequest();
    $post = $req->post;
    $user = new user($this->tfphp);
    $icon = $req->files->get("icon");

    try{
        // request test
        if($icon['name'] == "" || !preg_match("/^image\/(jpeg|png|gif)$/", $icon['type']) || $icon['error'] != 0){

            return $this->tfphp->getResponse()->responseJSON_CM(200, 1001041, "错误请求");
        }

        // create user
        $ret = $user->setIcon($this->permission->getLoginStatus()->userID,
            $req->files->get("icon"));
        switch ($ret){
            case 1:
                return $this->tfphp->getResponse()->responseJSON_CM(200, 1001042, "用户名不存在");
                break;
            case 3:
                return $this->tfphp->getResponse()->responseJSON_CM(200, 1001043, "保存用户头像失败");
                break;
        }
    }
    catch(\TypeError $e){

        return $this->tfphp->getResponse()->responseJSON_CM(200, 1001041, "错误请求");
    }

    // output
    return $this->tfphp->getResponse()->responseJSON_CM(200, 0, "OK");
}

视图模板

HTML代码

<!-- icon form begin -->
<div class="row login-form">
    <div class="col-sm-12">
        <h3 class="text-center">头像设置</h3>
        <p>选择一张图片作为您的头像,头像会被自动裁剪成正方形的照片</p>
        <form>
            <div class="form-group">
                <div class="py-3 text-center">
                    <img id="icon-image" src="<% $TFReq->server->BASE_URI %>images/usericon.jpg" style="width: 192px; height: 192px;" />
                </div>
                <input class="form-control-file" type="file" name="icon" style="display: none;" />
            </div>
            <div class="form-group">
                <button class="btn btn-primary btn-sm form-control">上传</button>
            </div>
        </form>
    </div>
</div>
<!-- icon form end -->

JS代码

// form
$('form').form({
    url: "<% $TFReq->server->BASE_URI %>api/member/icon/_save",
    method: "post",
    validations: [
        {type:"empty", name:"icon", msg:"请选择一张照片"}
    ],
    dataUrl: "<% $TFReq->server->BASE_URI %>api/member/icon",
    dataMethod: "post",
    dataMapRules: [
        {name:"icon", dataKey:"iconFileName"}
    ],
    onSuccess: function (d) {
        if(d.errcode == 0){
            document.location = '<% $TFReq->server->BASE_URI %>member/icon.htm';
        }
        else{
            $('form').tips({
                text:d.errmsg
            });
        }
    },
    onError: function (d) {
        $('form').tips({
            text:"服务器响应错误"
        });
    },
    onValidationError: function (form, name, msg) {
        $('form').tips({
            text:msg
        });
        $('form').find('[name="'+ name +'"]').focus();
    },
    onDataMap: function (form, name, data) {
        if(name == "icon"){
            $('#icon-image').attr('src', "<% $TFReq->server->BASE_URI %>userfiles/icon/"+data);

            return true;
        }

        return false;
    }
});
$('#icon-image').click(function () {
    var myFileIcon = $('[name="icon"]');
    myFileIcon.trigger('click');
});
$('[name="icon"]').bind('change', function(){
    var file = this.files[0];
    var rdr = new FileReader();
    rdr.onload = function () {
        $('#icon-image').attr('src', this.result);
    };
    rdr.readAsDataURL(file);
});

讲解

模型user

setIcon

首先测试用户是否存在,不存在就报错。

接着保证user_profile表记录一定存在。

再来根据上传图片文件组织新头像图片文件名。

然后根据旧头像图片文件名删除旧的头像。

随后根据新头像图片文件名使用TFImage对象的“中心裁剪”法裁剪图片合适用户头像的尺寸。

最后将新头像图片文件名存储到user_profile表里面。

接口控制器

doSave

首先检查输入参数是否正确,不正确就报错。

然后使用模型user的setIcon方法上传用户头像。

视图模板

HTML代码

这里面是一个img标签,一个隐藏的文件上传控件,还有一个提交按钮。

JS代码

前面是标准的form控件负责表单是处理功能。

第二行命令是通过给img标签增加点击事件的方法,用img标签起到文件上传控件的作用。

最后一行命令是重点,当用户选择了一个图片文件的时候,这个图片的内容会在头像区域预览出来。

效果

这是默认头像效果

adcad1c48abba844.jpg

总结

今天福哥带着童鞋们实现了设置用户头像的功能,从这一节课里面大家学会了如何使用HTML的文件上传控件,学会了如何使用JS处理图片,学会了如何使用PHP处理图片。

下一课,我们要完成用户密码的修改功能,这个相对比较简单~~