本书未发布

51. 上传图片

未匹配的标注

简介

在本节里,我们开发一个上传图片小组件,将在下节里使用该组件完成编辑头像功能。

需求分解

  1. HTML 默认的上传文件(input file)控制不能预览上传图片;
  2. 我们使用 AJAX 提交表单处理上传文件比较麻烦。

因为上面两点原因,所以我们开发一个上传文件的小组件,然后使用Iframe把引用该组件来实现用户头像上传功能。在本节我们先实现该组件的开发,下一节再介绍上传头像功能。

这个小组件的要求如下:

  • 上传图片成功后可以查看上传图片缩略图;
  • 为了使用广泛可以支持非图片格式文件上传。

图一是我们使用浏览器直接访问我们要实现功能的页面效果,图二是使用 IFrame 把上传图片页面引入到编辑表单的使用效果。从上图们可以看到当在 IFrame 里引用上传图片功能时,如果该表单已经有上传图片时显示已上传图片,当没有有图片时显示默认图片。

本节开发的功能有点复杂(特别是控制器代码),所以请大家结合下一节的上传头像功能多学习几遍。

数据模型

我们新建一个Upload模型类来处理上传文件保存逻辑,需要注意的是这个模型没有对应的数据库表,所以它不是继承 think\Model

application/common/model/Upload.php

<?php

namespace app\common\model;

class Upload
{
   /**
    * 保存上传图片
    * @Author   zhanghong(Laifuzi)
    * @DateTime 2019-02-21
    * @param    File               $file         文件信息
    * @return   array
    */
    static public function saveImage($file){
        // 所有上传文件都保存在项目 public/upload 目录里
        $local_dir = 'uploads';
        $ds = DIRECTORY_SEPARATOR;
        $info = $file->rule('md5')->move($local_dir);
        $save_name = $info->getSaveName();
        $save_path = $ds.$local_dir.$ds.$save_name;

        return [
            'ext' => $info->getExtension(),
            'save_path' => $save_path,
            'sha1' => $info->hash("sha1"),
            'md5' => $info->hash("md5"),
            'size' => $info->getSize(),
            'origin_name' => $file->getInfo('name'),
        ];
    }
}

以上,我们主要使用 ThinkPHP 的 上传文件 功能实现文件上传,当文件上传成功后返回文件存储信息。

控制器

同理,我们新建控制器 index/Upload 完成上传图片功能:

$ php think make:controller index/Upload

在该控制器里只需要 createsave 方法。另外因为 createsave 方法的主体代码相同,所以在这里我们把主体方法重构成一个新方法。

application/index/controller/Upload.php

<?php

namespace app\index\controller;

use think\Request;
use app\common\model\Upload as UploadModel;

class Upload extends Base
{
    public function create(Request $request)
    {
        return $this->save_image($request);
    }

    public function save(Request $request)
    {
        return $this->save_image($request);
    }

    private function save_image($request)
    {
        // 绑定控制名称
        $backcall = $request->param('backcall');
        // 图片预览宽度(px)
        $width = $request->param('width', 100);
        // 图片预览高度(px)
        $height = $request->param('height', 100);

        if($request->isPost()){
            // 保存上传图片
            $file = $request->file('image');
            $upload_info = UploadModel::saveImage($file);
            // 保存成功的图片路径
            $image = $upload_info['save_path'];
        }else{
            // 当前图片路径
            $image = $request->param('image');
        }

        $this->assign('backcall', $backcall);
        $this->assign('width', $width);
        $this->assign('height', $height);
        $this->assign('image', $image);
        return $this->fetch('create');
    }
}

路由

在配置文件里定义控制方法访问路由规则:

route/route.php

<?php
.
.
.
// 上传图片
Route::get('upload', 'upload/create')->name('upload.create');
Route::post('upload', 'upload/save')->name('upload.save');

视图模板

创建 upload/create 视图页面:

application/index/view/upload/create.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>图片上传</title>
    <style type="text/css">
        html,body{margin:0;padding:0;width:{$width}px;height:{$height}px;}
        .uploadpic{position:relative;}
        #ImgPr{border:0;}
        .puloadpic{position:absolute;width:{$width}px;height:{$height}px;}
        .inputfile{position:absolute;top:0px;left:0px;z-index:100;-moz-opacity:0.3;opacity:0.3;filter: alpha(opacity=30);background:#4f99c6;cursor:pointer;}
        #uploadicon{font-size:200px;overflow:hidden;display:block;width:{$width}px;height:{$height}px;-moz-opacity:0.0;opacity:0.0;filter: alpha(opacity=0);cursor:pointer;}
        .uploadtext{position:absolute;top:0px;left:0px;z-index:50;color:red;font-weight:bold;text-align:center;width:{$width}px;height:{$height}px;line-height:{$height}px;cursor:pointer;overflow:hidden;}
    </style>
</head>
<body>
    <form enctype="multipart/form-data" id="PostMe" action="" method="post" name="upform">
        <input type="hidden" value="{$width}" name="width">
        <input type="hidden" value="{$height}" name="height">
        <input type="hidden" value="{$backcall}" name="backcall">
        <div class="uploadpic">
            {empty name='$image'}
                <img id="ImgPr" src="/static/assets/index/images/nopic.gif"  width="{$width}" height="{$height}">
                <div class="uploadtext">上传图片</div>
            {else /}
                <img id="ImgPr" src="{$image}"  width="{$width}" height="{$height}">
                <div class="uploadtext">重新上传</div>
            {/empty}
            <div class="inputfile" title="点击上传图片">
                <input type="file" name="image" id="uploadicon" value="upload" accept="image/*"/>
            </div>
        </div>
    </form>
    {js href="/static/assets/plugins/jquery/jquery.min.js" /}
    <script type="text/javascript">
        $('#uploadicon').on("change",function(){
            $('#PostMe').submit();
        });
        var imgPath = "{$image}";
        var backCallSelector = '#{$backcall}';
        // 更新 parent 页面里回调字段值
        if(backCallSelector.length > 1 && $(backCallSelector, parent.document).length){
            $(backCallSelector, parent.document).val(imgPath);
        }

        // parent 页面里图片预览控件
        var $previewLink = $('#{$backcall}_preview', parent.document);
        if($previewLink.length){
            $previewLink.attr('href', imgPath);
            if(imgPath.length){
                if($previewLink.hasClass('hide')){
                    $previewLink.removeClass('hide');
                }
            }else{
                if(!$previewLink.hasClass('hide')){
                    $previewLink.addClass('hide');
                }
            }
        }
    </script>
</body>
</html>

效果展示

在本节里,我们暂时使用浏览器直接打开 上传文件 查看该功能是否正常。需要注意的是,使用浏览器直接访问时, URL 参数不能为空,另外请确认 public/upload 目录是否存在以及项目是否拥有该目录读写权限。

提交代码

因为,我们只需要把 public/upload 目录添加到版本管理里,而不需要把该目录内用户上传的文件也添加到版本管理里,所以我们在该目录里创建 .gitignore 文件让 Git 忽略该目录内的所有文件和子目录。

public/uploads/.gitignore

*
!.gitignore

下面把代码纳入到版本管理:

$ git add -A
$ git commit '上传图片'

本文章首发在 LearnKu.com 网站上。

上一篇 下一篇
讨论数量: 0
发起讨论 只看当前版本


暂无话题~