# 一、前端上传图片至服务器本地
# 参考文档:
node 接收前端文件数据
# 1.1 前端部分
Html部分
<form action="http://127.0.0.1:5000/upload" method="POST" id="img_form" enctype="multipart/form-data">
<div class="img_box">
<img src="##" alt="图片预览">
</div>
<input type="file" name="img_info" id="img_info" multiple><br>
<input type="submit" value="提交上传" id="img_submit">
</form>
1
2
3
4
5
6
7
2
3
4
5
6
7
注意:提交表单中有文件的话,必须要指定from 的属性 enctype = "multipart/form-data"
如果想要一次上传多个文件,需要指定 input 控件的属性 multiple = true
1
2
2
# 1.2 node 部分
# 引入 nultiparty模块
装包
npm install multiparty -S
1
引包
const multiparty = require('multiparty')
1
配置
# 1. 实例化一个文件对象
let form = new formidable.IncomingForm();
1
# 2. 配置文件的保存路径
form.uploadDir = "保存文件路径"
1
# 处理上传文件
//引入express
const express=require("express");
//引入multiparty
const multiparty=require("multiparty");
const fs = require('fs')
var router = express.Router();
router.post('/upload', function(req, res) {
// 设置文件存储路径
let fileRoot = './img/'
/* 生成multiparty对象,并配置上传目标路径 */
var form = new multiparty.Form();
form.encoding = 'utf-8';
form.uploadDir = fileRoot;
form.maxFilesSize = 2 * 1024 * 1024; // 文件大小 2M
// form.maxFields = 1000; //设置所有文件的大小总和
//上传后处理
form.parse(req, function(err, _fields, files) {
// var filesTemp = JSON.stringify(files, null, 2);
if(err) {
return res.json({code: 500, msg: '文件上传失败'})
}else {
// console.log('parse files:' + filesTemp);
for (const key in files) {
if (files.hasOwnProperty(key)) {
files = files[key];
}
}
files.forEach(element => {
var uploadedPath = element.path;
var dstPath = fileRoot + element.originalFilename;
//重命名为真实文件名
fs.rename(uploadedPath, dstPath, function(err) {
if(err) {
return console.log(uploadedPath + '文件重命名失败')
}
})
});
}
res.json({code: 200, msg: '文件上传成功'})
})
})
module.exports = router
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
上传文件的时候,nodejs默认会将上传的文件进行md5加密从而变成MD5名字格式的文件,并且连着扩展名也没了。因此,我们可以将上传的文件进行重新命名并且加上上传文件的后缀名。
注意:上面的代码是在一个路由文件上编写的,记得最后要在 app.js 中挂载一下,接口才能生效
1
2
2
# 1.3 实现上传图片后立即预览
# jquery核心代码
$(function(){
let img_info = $('##img_info')
img_info.change(function(){
let url = getObjectURL(img_info[0].files[0])
$('.img_box img').attr('src', url)
})
//建立一個可存取到該file的url
function getObjectURL(file) {
var url = null ;
if (window.createObjectURL != undefined) { // basic
url = window.createObjectURL(file) ;
} else if (window.URL != undefined) { // mozilla(firefox)
url = window.URL.createObjectURL(file) ;
} else if (window.webkitURL!=undefined) { // webkit or chrome
url = window.webkitURL.createObjectURL(file) ;
}
return url ;
}
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
如果直接打印文件对象的话,里面只存有图片的基本信息
file[0].value 中的路径是本地的盘符绝对路径,无法直接实现 img > src 预览
需要将文件对象传入 window.URL.createObjectUR(file) 函数中,经过处理返回后的值才是系统缓存中的路径,这个路径是可以呈现给用户预览的
1
2
3
2
3
# 1.4 multer 模块的使用
概述:
multer,这个插件是express的一个中间件,express1、2版本中本来是集成到express中的,express3之后就分离出来了,所以要使用multer必须会使用express
multer只负责解析表单数据,也就是请求头中携带content-type:multipart/form-data信息的请求才会处理,否则请注意multer不会运行
1
2
3
4
2
3
4
安装:
npm i multer -S
1
使用:
var express = require('express');
var multer = require('multer');
// 生成一个对象,凡是用这个对象生成的中间件,文件都会保存到uploads文件中
var upload = multer({ dest: 'uploads/' })
// 生成中间件,只能处理avatar的文件,文件的name只能是avatar,不是的话会报错
var dealavatar = upload.single('avatar');
var app = express()
// 调用中间件
app.post('/upload', dealavatar, function (req, res, next) {
// req.file is the `avatar` file
// req.body will hold the text fields, if there were any
res.send({
fileds:req.body,
files:req.file
})
})
app.listen(3000,()=>{
console.log("ok");
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
注意:
upload.single('avatar') 中指定的必须是前端表单中的 name 的值,如果不是的话,会报 MulterError: Unexpected field 的错误
1
2
2
# 二、七牛云之node本地文件上传
# 参考文档:
七牛云文档 - node_SDK
1
七牛云 - 封装node方法
# 2.1 node 部分
# 引入 qiniu 模块
安装
npm i qiniu -D
1
引包
const qiniu = require('qiniu')
1
# 上传指定单文件
创建一个 app.config.js 用于配置常用变量
module.exports = {
cdn: {
ak: 'rkuaLLzzWwzony5bl24nkfMwAtltZcudQMjbfYzS', // 七牛云的 AccessKey
sk: 'IixKN8uK_F15OscqZ1fDqKSCdu4di4PVgtCKUoqm', // 七牛云的 SecretKey
bucket: 'sp-zixuan2018', // 需要跟资源空间名一致
src: 'q1vorw6kn.bkt.clouddn.com'
// 上传地址,测试域名有效期30天 -2019.01.02到期
}
}
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
//上传文件 upload.js
const qiniu = require('qiniu')
const path = require('path')
const cdnConfig = require('./app.config').cdn
const {
ak, sk, bucket
} = cdnConfig
// 配置七牛云鉴权对象 mac
const mac = new qiniu.auth.digest.Mac(ak, sk);
let config = new qiniu.conf.Config()
config.zone = qiniu.zone.Zone_z2 // 代表华南区域的机房
// 构建上传方法
const doUpload = (key, file) => {
const options = {
scope: bucket + ':' + key, // key 为保存的文件名
};
const formUpload = new qiniu.form_up.FormUploader(config)
const putExtra = new qiniu.form_up.PutExtra()
const putPolicy = new qiniu.rs.PutPolicy(options)
const uploadToken = putPolicy.uploadToken(mac)
return new Promise((resolve, reject) => {
// 这里的 file 为字符串 C:\Users\倚天惊鸿网络丶紫炫\Desktop\imgNode\img\测试图片.jpg
formUpload.putFile(uploadToken, key, file, putExtra, (err, data, info) => {
if(err){
return reject(err)
}
if(info.statusCode === 200){
resolve(data)
}else{
reject(body)
}
})
})
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
const filePath = path.join(__dirname, './img/测试图片.jpg')
doUpload('测试图片2.png', filePath)
.then(res => console.log(res))
.catch(err => console.log(err))
1
2
3
4
5
6
2
3
4
5
6
- 一般会将 `doUpload `封装或原样导出,在 `app.js` 或 `router.js` 中引入调用
1
# 多文件上传
相比于单文件上传,多文件上传需要封装一个方法
// 封装多文件上传方法
const uploadAll = (dir, prefix) => {
let files = fs.readdirSync(dir)
console.log(files)
files.forEach(file => {
const filePath = path.join(dir, file)
const key = prefix ? `${prefix}/${file}` : file
if(fs.lstatSync(filePath).isDirectory()) {
return uploadAll(filePath, key)
}
doUpload(key, filePath)
.then(res => console.log(res))
.catch(err => console.log(err))
})
}
// 调用方法
uploadAll(path.join(__dirname, './img'))
//多次上传图片会覆盖前面已存在的同名图片
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 三、七牛云之资源管理
# 3.1 创建 BucketManager 资源管理器
官方参考文档
资源管理相关的操作首先要构建 BucketManager
对象:
var mac = new qiniu.auth.digest.Mac(accessKey, secretKey);
var config = new qiniu.conf.Config();
//config.useHttpsDomain = true;
config.zone = qiniu.zone.Zone_z0;
// 构建资源管理器
var bucketManager = new qiniu.rs.BucketManager(mac, config);
如果是批量操作,也需要先构建批量操作管理器
var batch = qiniu.batch();
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
# 3.2 查询
单文件查询
// 获取文件,传入想要获取文件的名称
const getFileInfo = function(key){
bucketManager.stat(bucket, key, function(err, respBody, respInfo) {
if (err) {
return console.log(err);
}
// console.log(respInfo)
if (respInfo.statusCode == 200) {
console.log(respBody);
} else {
console.log(respInfo.statusCode);
console.log(respBody.error);
}
});
}
getFileInfo('测试图片.jpg')
如果成功会输出以下内容
{
fsize: 164539, // 文件大小 单位B
hash: 'FqxZ2ZtnPjpTIW8LnePgjvQ_e0Jk', // 哈希值
md5: '3a669d7a845434c4d02d364be99cf6d5', // md5加密后数据
mimeType: 'image/jpeg', // 文件类型
putTime: 15753403648253244, // 上传的时间戳
type: 0
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
批量查询
//每个operations的数量不可以超过1000个,如果总数量超过1000,需要分批发送
var statOperations = [
qiniu.rs.statOp(srcBucket, 'qiniu1.mp4'),
qiniu.rs.statOp(srcBucket, 'qiniu2.mp4'),
qiniu.rs.statOp(srcBucket, 'qiniu3.mp4'),
qiniu.rs.statOp(srcBucket, 'qiniu4x.mp4'),
];
bucketManager.batch(statOperations, function(err, respBody, respInfo) {
if (err) {
console.log(err);
//throw err;
} else {
// 200 is success, 298 is part success
if (parseInt(respInfo.statusCode / 100) == 2) {
respBody.forEach(function(item) {
if (item.code == 200) {
console.log(item.data.fsize + "\t" + item.data.hash + "\t" +
item.data.mimeType + "\t" + item.data.putTime + "\t" +
item.data.type);
} else {
console.log(item.code + "\t" + item.data.error);
}
});
} else {
console.log(respInfo.statusCode);
console.log(respBody);
}
}
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# 3.3 修改
修改文件MimeType
var bucket = 'if-pbl';
var key = 'qiniu.mp4';
var newMime = 'video/x-mp4';
bucketManager.changeMime(bucket, key, newMime, function(err, respBody, respInfo) {
if (err) {
console.log(err);
//throw err;
} else {
//200 is success
console.log(respInfo.statusCode);
console.log(respBody);
}
});
修改文件存储类型
var bucket = 'if-pbl';
var key = 'qiniu.mp4';
//newType=0表示普通存储,newType为1表示低频存储
var newType = 0;
bucketManager.changeType(bucket, key, newType, function(err, respBody, respInfo) {
if (err) {
console.log(err);
//throw err;
} else {
//200 is success
console.log(respInfo.statusCode);
console.log(respBody);
}
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# 3.4 移动和重命名
移动操作本身支持移动文件到相同,不同空间中,在移动的同时也可以支持文件重命名。
唯一的限制条件是,移动的源空间和目标空间必须在同一个机房。
// 移动和重命名
const removeFile = function(srcKey, destKey, force = true){
var srcBucket = bucket;
var destBucket = bucket; // 目标空间名,可填写空区域的其他机房
var options = {
force: force // 强制覆盖已有同名文件
}
bucketManager.move(srcBucket, srcKey, destBucket, destKey, options, function(
err, respBody, respInfo) {
if (err) {
console.log(err);
//throw err;
} else {
//200 is success
console.log(respInfo.statusCode);
}
});
}
//removeFile('测试图片.jpg', '重命名后的测试图片.jpg')
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
var srcBucket = 'if-pbl';
var destBucket = srcBucket;
//每个operations的数量不可以超过1000个,如果总数量超过1000,需要分批发送
var moveOperations = [
qiniu.rs.moveOp(srcBucket, 'qiniu1.mp4', destBucket, 'qiniu1_move.mp4'),
qiniu.rs.moveOp(srcBucket, 'qiniu2.mp4', destBucket, 'qiniu2_move.mp4'),
qiniu.rs.moveOp(srcBucket, 'qiniu3.mp4', destBucket, 'qiniu3_move.mp4'),
qiniu.rs.moveOp(srcBucket, 'qiniu4.mp4', destBucket, 'qiniu4_move.mp4'),
];
bucketManager.batch(moveOperations, function(err, respBody, respInfo) {
if (err) {
console.log(err);
//throw err;
} else {
// 200 is success, 298 is part success
if (parseInt(respInfo.statusCode / 100) == 2) {
respBody.forEach(function(item) {
if (item.code == 200) {
console.log(item.code + "\tsuccess");
} else {
console.log(item.code + "\t" + item.data.error);
}
});
} else {
console.log(respInfo.deleteusCode);
console.log(respBody);
}
}
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# 3.5 复制文件副本
文件的复制和文件移动其实操作一样,主要的区别是移动后源文件不存在了,而复制的结果是源文件还存在,只是多了一个新的文件副本。
// 复制文件副本
const copyFile = function(srcKey){
var srcBucket = bucket;
var destBucket = bucket; // 目标空间名,可填写空区域的其他机房
// 自动生成复制后的文件名称 name.png ==> name_copy.png
let arr = srcKey.split('.')
arr[0] = arr[0] + '_copy'
let destKey = arr.join('.')
var options = {
force: true // 强制覆盖已有同名文件
}
bucketManager.copy(srcBucket, srcKey, destBucket, destKey, options, function(
err, respBody, respInfo) {
if (err) {
console.log(err);
//throw err;
} else {
//200 is success
console.log(respInfo.statusCode);
console.log(respBody);
}
});
}
//copyFile('重命名后的测试图片.jpg')
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
var srcBucket = 'if-pbl';
var srcKey = 'qiniu.mp4';
var destBucket = srcBucket;
//每个operations的数量不可以超过1000个,如果总数量超过1000,需要分批发送
var copyOperations = [
qiniu.rs.copyOp(srcBucket, srcKey, destBucket, 'qiniu1.mp4'),
qiniu.rs.copyOp(srcBucket, srcKey, destBucket, 'qiniu2.mp4'),
qiniu.rs.copyOp(srcBucket, srcKey, destBucket, 'qiniu3.mp4'),
qiniu.rs.copyOp(srcBucket, srcKey, destBucket, 'qiniu4.mp4'),
];
bucketManager.batch(copyOperations, function(err, respBody, respInfo) {
if (err) {
console.log(err);
//throw err;
} else {
// 200 is success, 298 is part success
if (parseInt(respInfo.statusCode / 100) == 2) {
respBody.forEach(function(item) {
if (item.code == 200) {
console.log(item.code + "\tsuccess");
} else {
console.log(item.code + "\t" + item.data.error);
}
});
} else {
console.log(respInfo.deleteusCode);
console.log(respBody);
}
}
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# 3.6 删除
单文件删除
// 删除文件
const deleteFile = function(srcKey){
var srcBucket = bucket;
bucketManager.delete(srcBucket, srcKey, function(err, respBody, respInfo) {
if (err) {
console.log(err);
//throw err;
} else {
console.log(respInfo.statusCode);
console.log(respBody);
}
});
}
deleteFile('重命名后的测试图片_copy.jpg')
批量删除
//每个operations的数量不可以超过1000个,如果总数量超过1000,需要分批发送
var deleteOperations = [
qiniu.rs.deleteOp(srcBucket, 'qiniu1.mp4'),
qiniu.rs.deleteOp(srcBucket, 'qiniu2.mp4'),
qiniu.rs.deleteOp(srcBucket, 'qiniu3.mp4'),
qiniu.rs.deleteOp(srcBucket, 'qiniu4x.mp4'),
];
bucketManager.batch(deleteOperations, function(err, respBody, respInfo) {
if (err) {
console.log(err);
//throw err;
} else {
// 200 is success, 298 is part success
if (parseInt(respInfo.statusCode / 100) == 2) {
respBody.forEach(function(item) {
if (item.code == 200) {
console.log(item.code + "\tsuccess");
} else {
console.log(item.code + "\t" + item.data.error);
}
});
} else {
console.log(respInfo.deleteusCode);
console.log(respBody);
}
}
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
# 3.7 设置或更新文件的生存时间
可以给已经存在于空间中的文件设置文件生存时间,或者更新已设置了生存时间但尚未被删除的文件的新的生存时间。
//设置更新时间
const setLifeTime = function(srcKey, days = 30){
var srcBucket = bucket;
// 设置生存时间,生存时间到了后删除 单位:天
bucketManager.deleteAfterDays(srcBucket, srcKey, days, function(err, respBody,
respInfo) {
if (err) {
console.log(err);
//throw err;
} else {
console.log(respInfo.statusCode);
console.log(respBody);
}
});
}
setLifeTime('重命名后的测试图片.jpg', 10)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 3.8 按文件前缀查询文件
可以按文件浅醉条件查询查一个符合条件的集合对象
@param options 列举操作的可选参数表
参数名 描述
prefix 列举的文件前缀
marker 上一次列举返回的位置标记,作为本次列举的起点信息
limit 每次返回的最大列举文件数量
delimiter 指定目录分隔符
经常用于分页、分类查询
// 按条件查询
const getFileByContidion = function(prefix){
var srcBucket = bucket;
var options = {
limit: 10, // 一次最多查10条
prefix: prefix, // 查询的文件前缀
};
bucketManager.listPrefix(srcBucket, options, function(err, respBody, respInfo) {
if (err) {
return console.log(err);
// throw err;
}
if (respInfo.statusCode == 200) {
//如果这个nextMarker不为空,那么还有未列举完毕的文件列表,下次调用listPrefix的时候,
//指定options里面的marker为这个值
var nextMarker = respBody.marker;
var commonPrefixes = respBody.commonPrefixes;
// console.log(nextMarker);
// console.log(commonPrefixes);
var items = respBody.items;
items.forEach(function(item) {
// console.log(item.key)
console.log(item)
});
} else {
console.log(respInfo.statusCode);
console.log(respBody);
}
});
}
getFileByContidion('子目录/')
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
# 3.9 抓取网络资源到空间
可以将网络上的链接资源直接抓取到存储空间
// 抓取网络资源到空间
const fetchSrc = function(resUrl, key){
var resUrl = 'http://devtools.qiniu.com/qiniu.png';
var srcBucket = bucket;
bucketManager.fetch(resUrl, srcBucket, key, function(err, respBody, respInfo) {
if (err) {
console.log(err);
//throw err;
} else {
if (respInfo.statusCode == 200) {
console.log(respBody);
} else {
console.log(respInfo.statusCode);
console.log(respBody);
}
}
});
}
fetchSrc('https://www.baidu.com/img/bd_logo1.png?where=super', 'baidu-logo.png')
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 3.10 更新镜像空间的内容
var bucket = "sp-zixuan2018";
var key = "qiniu.mp4";
bucketManager.prefetch(bucket, key, function(err, respBody, respInfo) {
if (err) {
console.log(err);
//throw err;
} else {
//200 is success
console.log(respInfo.statusCode);
}
});
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11