上传图片分别有uni.uploadFile方法和base64方法,后端用nodejs作服务。
- uni.uploadFile方法,前端只负责上传图片,后端接收图片后进行压缩保存。(H5,安卓能正常使用)
- base64方法,前端先压缩图片和转换成base64再上传,后端只接收保存。(安卓不能上传图片)
uni.uploadFile方法
- 前端uniapp代码
<template>
<view>
<view class='pages'>
<view class='father_view'>
<view class='son_view'>
<view class="title-bg">想说就说(最多120个汉字):</view>
<textarea class="textarea-bg" v-model="text1" @blur="inputText" placeholder="请输入留言" />
</view>
</view>
<!-- 图片 -->
<view class="images_box">
<block v-for="(item, index) in imglist" :key="index">
<view class='img-box'>
<image class='img' :src='item' mode='aspectFill'></image>
<view class='img-delete' @click='imgDelete1' :data-delindex="index">
<image class='img' src='../../static/delect.png' ></image>
</view>
</view>
</block>
<view class='img-box' @click='addPic1' v-if="imglist.length<9">
<image class='img' src='../../static/add_image.png'></image>
</view>
</view>
<button @click='uploadimage'>上传图片</button>
<button @click='viewmemo'>查看留言</button>
</view>
</view>
</template>
<script>
export default {
data() {
return {
imglist:[],//选择图片后生成的临时地址数组
text1:'',
imglist00:[]
}
},
onLoad() { },
methods: {
//*获取文本框内容*//
inputText:function (e) {
this.text1 = e.detail.value
},
//*选择图片*//
addPic1: async function() {
let that = this
uni.chooseImage({
count: 9, // 最多可以选择的图片张数,默认9
sizeType: ['compressed'], // original 原图,compressed 压缩图,默认二者都有
sourceType: ['album', 'camera'], // album 从相册选图,camera 使用相机,默认二者都有
success: function (res) {
if (that.imglist.length === 0) {
that.imglist = res.tempFilePaths
} else if (that.imglist.length < 9) {
that.imglist = that.imglist.concat(res.tempFilePaths); //concat追加到数组
}
}
})
},
//*显示选择后的图片*//
imgbox: function (e) {
this.imglist = e.detail.value
},
//* 删除已经选择的图片 *//
imgDelete1: function (e) {
let index = e.currentTarget.dataset.delindex; //获取要删除的图片的下标,否则删除的永远是第一张 ,对应 <view class='img-delect' @click='imgDelete1' :data-delindex="index">
this.imglist.splice(index, 1);
},
//*上传图片*//
uploadimage: function () {
let app = getApp()
let that = this
let upimg = require("./upimg.js") //引用当前目录下的自定义函数
let text2 = that.text1
if (text2 == '') {
uni.showToast({ //显示对话框
title: "请输入留言!",
icon: 'none',
duration: 1000,
});
} else {
if (that.imglist.length != 0 ) { //数组不为空的时候才执行
upimg.uploadimg({ //********* 调用引入的upimg.js文件uploadimg函数 ************
url: app.globalData.url+'/imgup', //全局变量,后端接口地址
path: that.imglist, //选取图片的临时地址数组
text: that.text1, //文本框内容
user: app.globalData.username,
});
uni.showToast({ //显示对话框
title: "上传中!",
icon: 'loading',
duration: 3000,
});
setTimeout(function () { //延迟执行,目的是等待上一步的uploadimg函数执行完成赋值给全局变量后再执行下面的代码
that.imglist = [] //清空选择的图片
that.text1 = '' //清空文本框的内容
}, 1000); //延迟时间
} else {
uni.showToast({
title: "请添加图片!",
icon: 'none',
duration: 1000,
})
}
}
},
viewmemo:function (e) {
uni.navigateTo({ //跳转到指定页面
url: "../memo/memo",
})
}
},
}
</script>
<style>
@import "./imgup.css";
</style>
多图上传函数 upimg.js 代码
//多张图片上传
function uploadimg(data) {
var app = getApp()
var imgurln=[]
var that = this
i = data.i ? data.i : 0, //当前上传的哪张图片
success = data.success ? data.success : 0, //上传成功的个数
fail = data.fail ? data.fail : 0; //上传失败的个数
uni.uploadFile({
url: data.url, //从调用页面传入 -- url: 'http://127.0.0.1:3000/' 后端接口地址
filePath: data.path[i], //从调用页面传入 --path: imgbox, 选取图片的地址数组
name: 'img', //文件名称可以自定义,要与后端配对使用:app.post('/',upload.single('img'),function(req,res,next)
formData: { //这里是上传图片时一起上传的数据
user: data.user,
memo: data.text //从调用页面传入 -- text:text1 文本框内容
},
success: (resp) => {
success++;//图片上传成功,图片上传成功的变量+1
//console.log(resp.data) //在调试窗口显示后端返回的图片名称
imgurln = imgurln.concat(app.globalData.url + resp.data); //以图片名称拼接成网址,并追加到数组imgurln
},
fail: (res) => { //失败
fail++;//图片上传失败,图片上传失败的变量+1
console.log('fail:' + i + "fail:" + fail);
},
complete: () => { //不论成功、失败都执行
i++; //这个图片执行完上传后,开始上传下一张
if (i == data.path.length) { //当图片传完时,停止调用
console.log('1>'+app.globalData.url);
console.log('执行完毕');
console.log('成功:' + success + " 失败:" + fail);
} else { //若图片还没有传完,则继续调用函数
//console.log(i);
data.i = i;
data.success = success;
data.fail = fail;
that.uploadimg(data);
}
}
});
}
module.exports.uploadimg = uploadimg; //把uploadimg函数暴露,才能在其它js文件引用此函数。
遍历数组方式上传多图更简单,不必再调用自定义多图上传函数。
`
- 后端nodejs代码
var express=require('express')
var app=express()
var router=express.Router()
var multer=require('multer')
var path=require('path')
var image = require("imageinfo")
var mssql =require("mssql") //引用mssql
var config = require('../config.js') //引用config.js文件
var request = require("request")
var storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, 'public/imgtemp/'); // 接收到文件后的保存路径,需要手动创建
},
filename: function (req, file, cb) {
cb(null, Date.now() + "-" + file.originalname); // 将保存文件名设置为: 时间戳 + 文件原始名,比如 151342376785-123.jpg
}
});
var upload = multer({ storage: storage })
var type = upload.single('img') //与前端的name:'img'对应
//* 接收图片 *//
router.post('/',type,function(req,res,next){
console.log('01>>',req.file);
console.log('02>>',req.body);
//console.log('03>>',req.file.path);
res.send(req.file.filename); //向前端返回当前上传成功的文件名
mssql.connect(config, function (err) { //连接数据库
if (err) {
console.log('数据库连接失败')
return callback(err);
} else {
//console.log('数据库连接成功')
var user1 = req.body.user ;
var memo1 = req.body.memo ; //req.body 解析前端formdata传递过来的数据 //req.query 解析前端data传递过来的数据
var filename1 = req.file.filename ;
var request = new mssql.Request();
request.query("INSERT INTO wx_bbs (username,memo,filename) VALUES ('"+ user1 +"','"+ memo1 +"','"+ filename1 +"')",function (err, recordsets,returnValue) {
if (err) {
console.log(err);
return
} else {
//str = JSON.stringify(recordsets.recordset); //将查询结果转换成json格式
//res.send(str); //响应请求,将数据发送到前端
console.log('执行sql脚本成功'); //在调试窗口显示
}
})
};
mssql.end; //结束连接数据库
});
// 压缩图片并删除原图 //
var images = require("images")
var fs=require('fs')
var name = './public/imgtemp/' + req.file.filename; //原图像文件完整路径
var outName = './public/imgup/' + req.file.filename; //压缩后的图像文件完整路径
images(name) //加载原图像文件
.size(1000) //等比缩放图像到1000像素宽
.draw(images("./public/png/logo.png"), 10, 10) //在(10,10)处绘制Logo
.save(outName, {quality:60}) //保存图片到文件,图片质量为60%
fs.unlink(name,function(err){ //删除原图
if(err){
console.log('删除失败');
return;
}
console.log('原图删除成功');
})
})
module.exports = router; //暴露模块,其它地方才能调用此模块
base64方法
- 前端uniapp代码
<template>
<view>
<view class='pages'>
<view class='father_view'>
<view class='son_view'>
<view class="title-bg">想说就说(最多120个汉字):</view>
<textarea class="textarea-bg" v-model="text1" @blur="inputText" placeholder="请输入留言" />
</view>
</view>
<!-- 图片 -->
<view class="images_box">
<block v-for="(item, index) in imagearr" :key="index">
<view class='img-box'>
<image class='img' :src='item' mode='aspectFill'></image>
<view class='img-delete' @click='imgDelete1' :data-delindex="index">
<image class='img' src='../../static/delect.png' ></image>
</view>
</view>
</block>
<view class='img-box' @click='addPic1' v-if="imagearr.length<9">
<image class='img' src='../../static/add_image.png'></image>
</view>
</view>
<button @click='uploadimage'>上传图片</button>
<button @click='viewmemo'>查看留言</button>
</view>
</view>
</template>
<script>
export default {
data() {
return {
imagearr: [],
text1:''
}
},
methods: {
// 获取文本框内容
inputText:function (e) {
var app = getApp()
app.globalData.memo = e.detail.value
this.text1 = e.detail.value
},
// 选择图片
addPic1: async function() {
let that = this
uni.chooseImage({
count: 9,
sizeType: ['original', 'compressed'],
sourceType: ['album','camera'],
success: (res) => {
if (that.imagearr.length === 0) {
that.imagearr = res.tempFilePaths
} else if (that.imagearr.length < 9) {
that.imagearr = that.imagearr.concat(res.tempFilePaths); //concat追加到数组
}
}
});
},
// 上传图片
uploadimage: function () {
let that = this
let text2 = that.text1
if (text2 == '') {
uni.showToast({ //显示对话框
title: "请输入留言!",
icon: 'none',
duration: 1000,
});
} else {
if (that.imagearr.length != 0 ) { //数组不为空的时候才执行
that.imgCompress(that.imagearr) //调用函数imgCompress
setTimeout(function () { //延迟执行
that.imagearr = [] //清空选择的图片
that.text1 = '' //清空文本框的内容
}, 3000); //延迟时间
uni.showToast({ //显示对话框
title: "上传中!",
icon: 'loading',
duration: 3000,
});
} else {
uni.showToast({
title: "请添加图片!",
icon: 'none',
duration: 1000,
})
}
}
},
// 循环调用压缩图片
imgCompress(tempFilePaths) {
let compressImgs = [];
let results = [];
tempFilePaths.forEach((item, index) => {
compressImgs.push(new Promise((resolve, reject) => {
// #ifndef H5
uni.compressImage({
src: item,
quality: this.quality,
success: res => {
//console.log('compressImage', res.tempFilePath)
results.push(res.tempFilePath);
resolve(res.tempFilePath);
},
fail: (err) => {
//console.log(err.errMsg);
reject(err);
},
complete: () => {
//uni.hideLoading();
}
})
// #endif
// #ifdef H5
//调用压缩图片函数 canvasDataURL
this.canvasDataURL(item, {
quality: this.quality / 100
}, (base64Codes) => {
//this.imgUpload(base64Codes);
results.push(base64Codes);
resolve(base64Codes);
})
// #endif
}))
})
Promise.all(compressImgs) //执行所有需请求的接口
.then((results) => {
//uni.hideLoading();
//this.imgUpload(results);
})
.catch((res, object) => {
//uni.hideLoading();
});
},
//压缩图片
canvasDataURL(path, obj, callback) {
var img = new Image();
img.src = path;
img.onload = function() {
var that = this;
// 默认按比例压缩
var w = that.width,
h = that.height,
scale = w / h;
w = obj.width || w;
h = obj.height || (w / scale);
var quality = 0.3; // 图片质量 quality值越小,所绘制出的图像越模糊
//生成canvas
var canvas = document.createElement('canvas');
var ctx = canvas.getContext('2d');
// 创建属性节点
var anw = document.createAttribute("width");
anw.nodeValue = w;
var anh = document.createAttribute("height");
anh.nodeValue = h;
canvas.setAttributeNode(anw);
canvas.setAttributeNode(anh);
ctx.drawImage(that, 0, 0, w, h);
if (obj.quality && obj.quality <= 1 && obj.quality > 0) {
quality = obj.quality;
}
var base64 = canvas.toDataURL('image/jpeg', quality);
callback(base64); // 回调函数返回base64的值
//console.log(base64);
var app = getApp()
var user = app.globalData.username;
var url = app.globalData.url;
var memo = app.globalData.memo;
//向后端上传数据
uni.request({
url: url+'/imgup',
data: {
img64: base64 ,
user: user ,
memo: memo
},
method: 'POST',
header: { 'content-type': 'application/json' },
success: (res) => { //请求成功
console.log(res.data) //在设计调试窗口显示后端返回的结果
},
fail: () => {
},
complete: () => {
}
})
}
},
// 删除已经选择的图片
imgDelete1: function (e) {
let index = e.currentTarget.dataset.delindex;
//获取要删除的图片的下标,否则删除的永远是第一张 ,对应 <view class='img-delect' @click='imgDelete1' :data-delindex="index">
this.imagearr.splice(index, 1);
},
// 查看留言
viewmemo:function (e) {
uni.navigateTo({ //跳转到指定页面
url: "../memo/memo",
})
}
},
}
</script>
<style>
@import "./imgup.css";
</style>
- 后端nodejs代码
var express=require('express')
var app=express()
var router=express.Router()
var mssql =require("mssql") //引用mssql
var config = require('../config.js') //引用config.js文件
var fs=require('fs')
//* 接收图片 *//
router.post('/',function(req,res) {
var user1 = req.body.user ;
var memo1 = req.body.memo ;
var imgdata = req.body.img64 ; //接收前端POST过来的base64
var base64Data = imgdata.replace(/^data:image\/\w+;base64,/, ""); //过滤 data:image/jpeg;base64,
var dataBuffer = new Buffer(base64Data, 'base64');
var save_path = './public/imgup/' //保存路径
var file_name = Date.now() + '.png' //以时间戳做文件名
var save_name = save_path + file_name
fs.writeFile(save_name, dataBuffer, function(err) {
if(err){
res.send(err);
}else{
res.send("后端图片保存成功!");
console.log("保存成功!")
}
});
mssql.connect(config, function (err) { //连接数据库
if (err) {
console.log('数据库连接失败')
return callback(err);
} else {
//console.log('数据库连接成功')
var request = new mssql.Request();
request.query("INSERT INTO wx_bbs (username,memo,filename) VALUES ('"+ user1 +"','"+ memo1 +"','"+ file_name +"')",function (err, recordsets,returnValue) {
if (err) {
console.log(err);
return
} else {
//str = JSON.stringify(recordsets.recordset); //将查询结果转换成json格式
//res.send(str); //响应请求,将数据发送到前端
console.log('执行sql脚本成功'); //在调试窗口显示
}
})
};
mssql.end; //结束连接数据库
});
})
module.exports = router; //暴露模块,其它地方才能调用此模块
注意:要在程序入口加入下图方框中的代码,不然解析不了前端POST过来的数据