一、前期准备
1.七牛云注册
进入七牛云官网进行注册,地址:https://www.qiniu.com/
2.获取密钥
完成注册后登录七牛云并进入控制台,在右上角头像处下拉选择【密钥管理】获取AccessKey和SecretKey
3. 新建空间
选择【对象存储】下的【空间管理】,点击“新建空间”按钮
完成后点击“空间名称”会跳转到“空间概览”界面,如下图:
域名管理:若需要接入自己已备案好的域名,可以在【域名管理】中进行配置,具体如何配置可参考如下链接
https://developer.qiniu.com/fusion/1322/how-to-configure-cname-domain-name
二、在Laf中接入七牛云
登录Laf云开发平台,在应用列表中选择一个应用后点击【开发】按钮,进入Laf应用开发IDE
1. 添加NPM依赖
点击左下角【NPM依赖】处的“+”按钮,在弹框中搜索“qiniu”,选中第一个后再点击“保存并重启”,等待3秒左右依赖会添加完成
2. 添加函数
点击左上角【函数列表】处的“+”按钮,在弹框中输入函数名(比如:file-upload),其他默认,完成后点击“确认”按钮,等待3秒左右函数会添加完成
说明:关于云函数的入门,可点击 https://doc.laf.run/guide/function/ 链接了解详情
3. 云函数完整代码
1)云函数“file-upload”完整代码如下:
import cloud from '@lafjs/cloud'
import { QiniuStore } from '@/qiniu-store'
const fs = require("fs")
export default async function (ctx: FunctionContext) {
const _body = ctx.body;
const _query = ctx.query;
const _type = _body.type ? _body.type : _query.type;
if (!_type) {
return resultData(-1, '参数type不能为空!');
}
const _files = ctx.files;
switch (_type) {
case 'uploadFile':
return await uploadFile(_files);
default:
return resultData(-1, '请检查参数type是否有误!');
}
}
async function uploadFile(files) {
console.log('uploadFile->files', files);
const _files = files;
if (!_files || _files.length == 0) {
return resultData(-1, '未上传文件!');
}
const fileInfo = _files[0];
if (!fileInfo.filename) {
return resultData(-1, '文件名称为空!');
}
if (!fileInfo.mimetype) {
return resultData(-1, '文件类型为空!');
}
if (!fileInfo.size || fileInfo.size > 5 * 1024 * 1024) {
return resultData(-1, '文件大小不能超过5M!');
}
try {
let fileData = await fs.readFileSync(fileInfo.path);
const res = await QiniuStore.uploadFile({ fileName: fileInfo.filename, fileData: fileData });
return res;
}
catch (e) {
return resultData(-1, '异常错误:' + e.message);
}
}
function resultData(code = -1, msg = '', data = null) {
return { code, msg, data }
}
说明:前端上传的文件在云函数的ctx.files里可以找到,以下是云函数接收到文件后ctx.files的打印结果范例。
console.log(ctx.files)
2)云函数“qiniu-store”完整代码如下:
import cloud from '@lafjs/cloud'
const moment = require('moment');
const qiniu = require('qiniu');
const accessKey = 'your access key';
const secretKey = 'your secret key';
const bucket = 'xxx';
const domain = 'http://xxx-xxx.com';
export class QiniuStore {
static async uploadFile(obj: any): Promise<object> {
if (!obj.fileData) {
return resultData(-1, '文件数据为空!');
}
const date = moment();
let key = 'uploadFiles/' + date.format('YYYYMMDD') + '/';
if (!obj.fileName) {
key += Math.ceil(Math.random() * 1000000000000) + '.jpg';
}
else {
key += obj.fileName;
}
try {
const mac = new qiniu.auth.digest.Mac(accessKey, secretKey);
const config = new qiniu.conf.Config();
config.zone = qiniu.zone.Zone_z2;
const formUploader = new qiniu.form_up.FormUploader(config);
const putExtra = new qiniu.form_up.PutExtra();
const options = {
scope: bucket + ':' + key,
};
const putPolicy = new qiniu.rs.PutPolicy(options);
const uploadToken = putPolicy.uploadToken(mac);
const res = await new Promise((resolve, reject) => {
formUploader.put(uploadToken, key, obj.fileData, putExtra, function (respErr,
respBody, respInfo) {
if (respErr) {
console.log('respErr', respErr);
reject(null);
}
if (respInfo.statusCode == 200) {
console.log('respBody', respBody);
resolve(domain + '/' + key);
} else {
console.log('respInfo', respInfo);
console.log('respBody', respBody);
reject(null);
}
});
});
return await this.getFileInfo(key);
}
catch (e) {
console.log('七牛云上传异常', e.message);
return resultData(-1, '上传异常:' + e.message);
}
}
static async getFileInfo(key: string): Promise<object> {
if (!key) {
return resultData(-1, '文件key不能为空');
}
try {
const mac = new qiniu.auth.digest.Mac(accessKey, secretKey);
const config = new qiniu.conf.Config();
config.zone = qiniu.zone.Zone_z2;
const bucketManager = new qiniu.rs.BucketManager(mac, config);
const res = await new Promise((resolve, reject) => {
bucketManager.stat(bucket, key, function (err, respBody, respInfo) {
if (err) {
console.log('err', err);
reject(null);
} else {
if (respInfo.statusCode == 200) {
console.log('respBody', respBody);
let lastIndex = key.lastIndexOf('/');
resolve({
fileName: key.substr(lastIndex + 1),
fileUrl: domain + '/' + key,
fileType: respBody.mimeType,
fileSize: respBody.fsize
});
} else {
console.log('respInfo', respInfo);
console.log('respBody', respBody);
reject(null);
}
}
});
});
return resultData(0, 'success', res);
}
catch (e) {
console.log('七牛云获取文件信息异常', e.message);
return resultData(-1, '获取文件信息异常:' + e.message);
}
}
}
function resultData(code = -1, msg = '', data = null) {
return { code, msg, data }
}
其中关于Zone对象和机房的关系如下:
机房 Zone对象
华东 qiniu.zone.Zone_z0
华北 qiniu.zone.Zone_z1
华南 qiniu.zone.Zone_z2
北美 qiniu.zone.Zone_na0
完成后将两个云函数进行发布。
三、最终效果
使用Apipost调用文件上传(file-upload)接口如下:
然后返回到七牛云控制台,进入【对象存储】-【空间管理】下的“文件管理”,查看刚刚上传过的文件,如下图: