最近小程序后台做了个图片集功能,方便用户上传多张图片,主要用在产品详情的特效图片。这里做个记录。
体验:测试上传了5张图片,之后做个删除的功能,用php unlink删除文件,本地电脑可以正常删除,线上是linux系统删除图片时提示文件不存在或目录为空。
研究了一下,发现有趣的规则。
$filename = str_replace('/', '\\', $filename);//window,本机可以
改成如下就可以了
$filename = str_replace('\\', '/', $filename);//linux,window
那么本机和线上通用的就是这样的路径:/www/wwwroot/test/upload/20200605/181904a34185a6d440f242e5eccca91243f578.png
数据表
banners字段:
20200605/181904a34185a6d440f242e5eccca91243f578.png;20200605/18232617ad9dc4dabba1e7018120db3cb984a4.jpg;
前端
<?php
use yii\helpers\Url;
?>
<form class="layui-form" οnsubmit="return doSubmit()">
<div class="layui-form-item">
<label class="layui-form-label">产品图片</label>
<div class="layui-upload">
<div class="layui-upload-list">
<img class="layui-upload-img"
id="preview"
style="width: auto; height: 50px;"
src="<?= $data['imageurl'] ? '/statics/uploads/'.$data['imageurl'] : '/statics/images/upload.png'?>">
<p id="error"></p>
</div>
<input id="imageurl" disabled type="hidden" name="imageurl" value="<?= $data['imageurl']?>">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">产品名称</label>
<div class="layui-input-block">
<input id="name" type="text" name="name" lay-verify="name" placeholder="请输入产品名称" class="layui-input" value="<?= $data['name']?>">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">产品编号</label>
<div class="layui-input-block">
<input id="pro_num" type="text" name="pro_num" lay-verify="pro_num" placeholder="请输入产品编号" class="layui-input" value="<?= $data['pro_num']?>">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">产品简介</label>
<div class="layui-input-block">
<input id="intro" type="text" name="intro" lay-verify="intro" placeholder="请输入产品简介" class="layui-input" value="<?= $data['intro']?>">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">所属分类</label>
<div class="layui-input-block">
<select name="cate_id" id="cate_id" lay-filter="cate_id">
<option value="0">请选择</option>
<?php
foreach ($cate_item as &$t_item) {
?>
<option value="<?= $t_item['id'] ?>"<?= $data['cate_id'] == $t_item['id'] ? 'selected' : '' ?>><?= $t_item['name'] ?></option>
<?php
}
?>
</select>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">所属品牌</label>
<div class="layui-input-block">
<select name="brand_id" id="brand_id" lay-filter="brand_id">
<option value="0">请选择</option>
<?php
foreach ($brand_item as &$t_item) {
?>
<option value="<?= $t_item['id'] ?>"<?= $data['brand_id'] == $t_item['id'] ? 'selected' : '' ?>><?= $t_item['name'] ?></option>
<?php
}
?>
</select>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">产品价格</label>
<div class="layui-input-block">
<input id="price" type="text" name="price" lay-verify="price" placeholder="请输入产品价格" class="layui-input" value="<?= $data['price']?>">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">图片集:</label>
<div class="layui-input-block">
<?php if (!empty($data['banners'])) { ?>
<?php foreach ($data['banners'] as $item) { ?>
<img src="/statics/uploads/<?=$item?>" alt="" style="width: auto; height: 100px;">
<span class="picurl" data-url="<?=$item?>" style="margin-right: 10px;cursor:pointer;">删除</span>
<?php } ?>
<?php }else{ ?>
<div class="layui-form-mid layui-word-aux">暂无图片</div>
<?php } ?>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">上传图片:</label>
<div class="layui-input-block">
<div class="layui-upload">
<button type="button" class="layui-btn layui-btn-normal" id="uploader">选择多文件</button>
<button type="button" style="margin-left: 10px" class="layui-btn" id="upload_btn">开始上传</button>
<span style="margin-left: 10px; color: #999;">注:上传图片会覆盖掉原来的图片</span>
<div class="layui-upload-list">
<table class="layui-table">
<thead>
<tr><th>文件名</th>
<th>预览</th>
<th>大小</th>
<th>状态</th>
<th>操作</th>
</tr></thead>
<tbody id="previewer"></tbody>
</table>
</div>
</div>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">产品介绍</label>
<div class="layui-input-block">
<textarea id="editor_id" name="content" style="width: 100%;height: 500px" lay-verify="detail" placeholder="请输入产品介绍">
<?=$data['detail']?>
</textarea>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">排序</label>
<div class="layui-input-block">
<input id="rank" type="text" name="rank" lay-verify="rank" placeholder="请输入排序" class="layui-input" value="<?= $data['rank']?>">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">是否新品</label>
<div class="layui-input-block">
<input type="radio" id="is_new" name="is_new" value="1" title="是" <?= $data['is_new'] == 1 ? 'checked' : ''?> lay-filter="is_new" >
<input type="radio" id="is_new" name="is_new" value="0" title="否" <?= $data['is_new'] == 0 ? 'checked' : ''?> lay-filter="is_new" >
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">是否热卖</label>
<div class="layui-input-block">
<input type="radio" id="hot" name="is_hot" value="1" title="是" <?= $data['is_hot'] == 1 ? 'checked' : ''?> lay-filter="is_hot" >
<input type="radio" id="is_hot" name="is_hot" value="0" title="否" <?= $data['is_hot'] == 0 ? 'checked' : ''?> lay-filter="is_hot" >
</div>
</div>
<div class="layui-form-item">
<div class="layui-input-block" style="margin-left: 110px">
<button type="submit" class="layui-btn" lay-submit="">立即提交</button>
</div>
</div>
</form>
<script>
//多图片上传
var banners = [];
KindEditor.ready(function(K) {
window.editor = K.create('#editor_id');
});
layui.use('element', function(){
var element = layui.element;
});
layui.use(['form'], function() {
var form = layui.form, layer = layui.layer;
//自定义验证规则
form.verify({
// html中补充lay-verify属性
name: function (value) {
if (value.length <= 0) {
return '产品名称不能为空';
}
},
});
});
function doSubmit(){
var img = $('#imageurl').val();
var content = window.editor.html();
if (img.length == 0) {
alert('图片不能为空');
return false;
}
var data = {
name: $('#name').val(),
imageurl: $('#imageurl').val(),
cate_id: $('#cate_id').val(),
brand_id: $('#brand_id').val(),
pro_num: $('#pro_num').val(),
intro: $('#intro').val(),
content:content,
price: $('#price').val(),
rank: $('#rank').val(),
is_new: $('input[name="is_new"]:checked').val(),//$('#is_new').val(),
is_hot: $('input[name="is_hot"]:checked').val(),//$('#is_hot').val()
banners: banners.join(';')
};
console.log(banners);
var loading = layer.load(0);
var url = '<?= Url::toRoute('product/submit')?><?= $_GET['id'] ? '?id='.$_GET['id'] : ''?>';
$.post(url, data, function (res) {
layer.msg(res.message, {icon: res.code > 0 ? 6 : 5, time: 1000}, function () {
if (res.code > 0){
// layer.close(loading);
location.href='<?= Url::toRoute('product/index')?>';
}
layer.close(loading);
});
}, 'json');
return false;
}
layui.use(['form','upload'], function() {
var $ = layui.jquery, upload = layui.upload, form = layui.form;
//普通图片上传
var uploadInst = upload.render({
elem: '#preview'
, url: '/upload'
, before: function (obj) {
//预读本地文件示例,不支持ie8
obj.preview(function (index, file, result) {
$('#preview').attr('src', result); //图片链接(base64)
});
}
, done: function (res) {
console.log(res);
layer.msg(res.message, {icon: res.code > 0 ? 6 : 5, time: 1000});
//上传成功
if (res.code > 0){
$('input[type="hidden"]').val(res.filename);
}
}
, error: function () {
//失败状态,并实现重传
var demoText = $('#error');
demoText.html('<span style="color: #FF5722;">上传失败</span> <a class="layui-btn layui-btn-xs avatar-reload">重试</a>');
demoText.find('.avatar-reload').on('click', function () {
uploadInst.upload();
});
}
});
//多文件列表示例
var ListView = $('#previewer')
,uploadListIns = upload.render({
elem: '#uploader'
,url: '/upload'
,accept: 'file'
,multiple: true
,auto: false
,bindAction: '#upload_btn'
,choose: function(obj){
var files = this.files = obj.pushFile(); //将每次选择的文件追加到文件队列
//读取本地文件
obj.preview(function(index, file, result){
var tr = $(['<tr id="upload-'+ index +'">'
,'<td>'+ file.name +'</td>'
,'<td><img src="' + result + '" alt="'+ file.name +'"></td>'
,'<td>'+ (file.size/1014).toFixed(1) +'kb</td>'
,'<td>等待上传</td>'
,'<td>'
,'<button class="layui-btn layui-btn-xs demo-reload layui-hide">重传</button>'
,'<button class="layui-btn layui-btn-xs layui-btn-danger demo-delete">删除</button>'
,'</td>'
,'</tr>'].join(''));
//单个重传
tr.find('.demo-reload').on('click', function(){
obj.upload(index, file);
});
//删除
tr.find('.demo-delete').on('click', function(){
delete files[index]; //删除对应的文件
tr.remove();
uploadListIns.config.elem.next()[0].value = ''; //清空 input file 值,以免删除后出现同名文件不可选
});
ListView.append(tr);
});
}
,done: function(res, index, upload){
if(res.code > 0){ //上传成功
var tr = ListView.find('tr#upload-'+ index)
,tds = tr.children();
tds.eq(2).html('<span style="color: #5FB878;">上传成功</span>');
tds.eq(3).html(''); //清空操作
banners.push(res.filename);
return delete this.files[index]; //删除文件队列已经上传成功的文件
}
this.error(index, upload);
}
,error: function(index, upload){
var tr = ListView.find('tr#upload-'+ index)
,tds = tr.children();
tds.eq(2).html('<span style="color: #FF5722;">上传失败</span>');
tds.eq(3).find('.demo-reload').removeClass('layui-hide'); //显示重传
}
});
});
//删除图片集中的图片
$(function() {
$(".picurl").click(function() {
//var id = $(this).attr("curId"); //获得当前行ID
var img = $(this).data("url");
if(img.length == 0){
alert('图片不能为空');
return false;
}
var data = {
picurl: img
};
var url = '<?= Url::toRoute('product/picdelete')?><?= $_GET['id'] ? '?id='.$_GET['id'] : ''?>';
$.post(url, data, function(res) {
console.log(res);
//return false;
if (res.code > 0) {
alert(res.message);
var urlJump = '<?= Url::toRoute('product/submit')?><?= $_GET['id'] ? '?id='.$_GET['id'] : ''?>';
location.href = urlJump;
} else {
alert(res.message);
}
});
});
});
</script>
后端
<?php
namespace app\modules\admin\controllers;
use app\components\Controller;
use app\components\Request;
use app\components\Response;
use app\models\Product;
use app\models\Cate;
use app\helpers\UtilsHelper;
use app\helpers\ArrayHelper;
class ProductController extends Controller
{
public function actionIndex(){
//接受数据
$req = Request::getInstance();
if($req->isPost){
$page = $req->post('page',1);
$limit = $req->post('limit',20);
$keywords = $req->post("keywords" ,null);
$data=Product::find()
->where('status>=0');
if($keywords){
//$data= $data->andWhere(['like','name',$keywords])->orWhere(['id' => $keywords]);
$data= $data->andWhere(['or',['like','name',$keywords],['id' => $keywords]]);
}
$data=$data->orderBy('createtime desc')
->offset(($page - 1)* $limit)
->limit($limit)
->asArray()
->all();
//遍历数组时间输出
foreach($data as &$item){
$item['createtime']=date('Y-m-d H:i:s',$item['createtime']);
$cateData = Cate::find()
->where(['id' => $item['cate_id']])
->asArray()
->one();
$item['cate_name'] = $cateData['name'];
$item['is_new'] = $item['is_new'] == 1 ? '新品':'';
$item['is_hot'] = $item['is_hot'] == 1 ? '热卖':'';
}
$count = Product::find()->where('status>=0');
if ($keywords){
$count = $count->andWhere(['or',['like', 'name', $keywords],['id' => $keywords]]);
}
$count = $count->count();
$result=[
'code' => 0,
'msg' => '',
'count' => $count,
'data' => $data,
];
Response::getInstance()->format = Response::FORMAT_JSON;
return $result;
}
return $this->render('index');
}
/**
* 新增/编辑产品
* @return array|string
*/
public function actionSubmit(){
$req = Request::getInstance();
$id = $req->get('id');
$admin = \Yii::$app->session->get('admin_user');
if ($req->isPost) {
if ($id){
$data = Product::find()
->where(['id' => $id])
->asArray()
->one();
$post_data = [
'name' => $req->post('name'),
'cate_id' => $req->post('cate_id'),
'brand_id' => $req->post('brand_id'),
'pro_num' => $req->post('pro_num'),
'intro' => $req->post('intro'),
'detail' => $req->post('content'),
'price' => $req->post('price'),
'rank' => $req->post('rank'),
'is_new' => $req->post('is_new'),
'is_hot' => $req->post('is_hot'),
//'banners' => $banners,
];
//多图
$bannersNew = $req->post('banners');
if($bannersNew){
if($data['banners']){
$banners = explode(';',$data['banners']);
$banners = array_filter($banners);//去空
$banners = implode(';',$banners);
$post_data['banners'] = $banners.';'.$bannersNew;
}else{
$post_data['banners'] = $bannersNew;
}
}
//print_r($post_data);exit;
if ($req->post('imageurl')){
$post_data['imageurl'] = $req->post('imageurl');
}
$rst = Product::updateAll($post_data, ['id' => $id] );
UtilsHelper::setSystemLog($admin->id, '用户'.$admin->username.'修改了产品ID:'.$id);
}else{
//新增轮播图
$model = new Product();
$model->name = $req->post('name');
$model->cate_id = $req->post('cate_id');
$model->brand_id = $req->post('brand_id');
$model->pro_num = $req->post('pro_num');
$model->intro = $req->post('intro');
$model->detail = $req->post('content');
$model->price = $req->post('price');
$model->rank = $req->post('rank');
$model->is_new = $req->post('is_new');
$model->is_hot = $req->post('is_hot');
$model->banners = $req->post('banners');
if ($req->post('imageurl')){
$model->imageurl = $req->post('imageurl');
}
$model->status = 1;
$model->createtime = time();
$rst = $model->save(false);
UtilsHelper::setSystemLog($admin->id, '用户'.$admin->username.'添加了产品ID:'.$model->id);
}
Response::getInstance()->format = Response::FORMAT_JSON;
return [
'code' => $rst ? 200 : -1,
'message' => $rst ? '保存成功!' : '保存失败!'
];
}
$data = [];
if ($id){
$data = Product::find()
->where(['id' => $id])
->asArray()
->one();
if($data){
if($data['banners']){
$banners = explode(';', $data['banners']);
}
$data['banners'] = $banners?array_filter($banners):'';
}
}
$cate_item = [];
$cate_item = Cate::find()
->where('status > 0 and type=1')
->asArray()
->all();
$cate_item = ArrayHelper::getTree($cate_item);
//print_r($cate_item);exit;
foreach($cate_item as &$value){
if($value['level'] != 0){
$value['name'] = '|'.str_repeat('--', $value['level']).$value['name'];
}
}
//brand
$brand_item = [];
$brand_item = Cate::find()
->where('status > 0 and type=4')
->asArray()
->all();
//$brand_item = ArrayHelper::getTree($brand_item);
return $this->render('submit', ['data' => $data,'cate_item' => $cate_item,'brand_item' => $brand_item]);
}
/**
* 删除产品
* @return array
* 假删除 库中仍存在数据
*/
public function actionDelete(){
$req = Request::getInstance();
if ($req->isPost){
$id = $req->post('id');
//删除状态修改为-1
$rst = Product::updateAll(['status' => -1], ['id' => $id]);
$admin = \Yii::$app->session->get('admin_user')->id;
$message = '用户'.$admin->username.'删除了产品ID:'.$id;
//添加删除记录
UtilsHelper::setSystemLog($admin->id, $message);
Response::getInstance()->format = Response::FORMAT_JSON;
return [
'code' => $rst,
'message' => $rst ? '操作成功' : '操作失败'
];
}
}
/**
* 删除产品图集中的图片
* @return array
*/
public function actionPicdelete(){
$req = Request::getInstance();
if ($req->isPost){
$id = $req->get('id');
$picurl = $req->post('picurl');
$res = Product::find()
->where('status > 0 and id='.$id)
->asArray()
->one();
if(!$res['banners']){
return [
'code' => 0,
'message' => '失败'
];
}
$banners = explode(';', $res['banners']);
foreach ($banners as $key => $value) {
if($picurl === $value){
$filename = \Yii::$app->basePath.'/web/statics/uploads/'.$picurl;
$filename = str_replace('/', '\\', $filename);//window
$filename = str_replace('\\', '/', $filename);//linux,window
//exit(is_readable($filename));
//exit(is_writable($filename));
if(file_exists($filename)){
$do = unlink($filename);
if($do){
unset($banners[$key]);
}else{
exit('删除失败');
}
}else{
exit('文件不存在'.$filename);
}
}
}
$banners = implode(';', $banners);
//print_r($banners);exit;
$rst = Product::updateAll(['banners' => $banners], ['id' => $id]);
$admin = \Yii::$app->session->get('admin_user');
$message = '用户'.$admin->username.'删除了产品图片picurl:'.$picurl;
//添加删除记录
UtilsHelper::setSystemLog($admin->id, $message);
Response::getInstance()->format = Response::FORMAT_JSON;
return [
'code' => $rst,
'message' => $rst ? '删除成功' : '删除失败'
];
}
}
}