51. 上传图片
简介
在本节里,我们开发一个上传图片小组件,将在下节里使用该组件完成编辑头像功能。
需求分解
- HTML 默认的上传文件(input file)控制不能预览上传图片;
- 我们使用 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
在该控制器里只需要 create
和 save
方法。另外因为 create
和 save
方法的主体代码相同,所以在这里我们把主体方法重构成一个新方法。
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 '上传图片'
推荐文章: