uni-app小程序开发-文件上传

前言

https://uniapp.dcloud.net.cn/api/request/network-file.html

选择图片

1
2
3
4
5
uni.chooseImage({
success: (chooseImageRes) => {
const tempFilePaths = chooseImageRes.tempFilePaths;
}
});

选择并上传

小程序不支持同时上传多张

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
uni.chooseImage({
success: (chooseImageRes) => {
const tempFilePaths = chooseImageRes.tempFilePaths;
uni.uploadFile({
url: 'https://www.example.com/upload', //仅为示例,非真实的接口地址
filePath: tempFilePaths[0],
fileType:"image",
name: 'file',
formData: {
'user': 'test'
},
success: (uploadFileRes) => {
console.log(uploadFileRes.data);
}
});
}
});

上传到阿里OSS

在 uni-app 小程序开发中,直接使用 ali-oss SDK 可能会面临一些兼容性和环境问题,建议使用 REST API 进行操作,这样可以保证在小程序环境中的兼容性。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
const host = '<host>';
const signature = '<signatureString>';
const ossAccessKeyId = '<accessKey>';
const policy = '<policyBase64Str>';
const securityToken = '<x-oss-security-token>';
const key = '<object name>'

uni.chooseImage({
chooseImage: 1,
success: res => {
const path = res.apFilePaths[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
uni.uploadFile({
url: host,
fileType: 'image',
name: 'file',
filePath: path,
formData: {
key,
policy,
OSSAccessKeyId: ossAccessKeyId,
signature,
success_action_status: '200',
// 使用STS签名时传入securityToken。
'x-oss-security-token': securityToken
},
success: (res) => {
// 将上传成功状态码设置为200,默认状态码为204。
if (res.statusCode === 200) {
console.log('上传成功');
}
},
fail: err => {
console.log(err);
}
});

完全本地方案

小程序前端生成验证的签名,前端上传到阿里OSS。

需要把上传阿里的地址添加到小程序的白名单中。

但是不建议这样做,还是建议密钥一类的放在后端处理。

加密工具类

加密

1
npm install crypto-js --save

base64.js

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
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
export const Base64 = {
// private property
_keyStr : "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",

// public method for encoding
encode : function (input) {
var output = "";
var chr1, chr2, chr3, enc1, enc2, enc3, enc4;
var i = 0;

input = Base64._utf8_encode(input);

while (i < input.length) {

chr1 = input.charCodeAt(i++);
chr2 = input.charCodeAt(i++);
chr3 = input.charCodeAt(i++);

enc1 = chr1 >> 2;
enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
enc4 = chr3 & 63;

if (isNaN(chr2)) {
enc3 = enc4 = 64;
} else if (isNaN(chr3)) {
enc4 = 64;
}

output = output +
this._keyStr.charAt(enc1) + this._keyStr.charAt(enc2) +
this._keyStr.charAt(enc3) + this._keyStr.charAt(enc4);

}

return output;
},

// public method for decoding
decode : function (input) {
var output = "";
var chr1, chr2, chr3;
var enc1, enc2, enc3, enc4;
var i = 0;

input = input.replace(/[^A-Za-z0-9\+\/\=]/g, "");

while (i < input.length) {

enc1 = this._keyStr.indexOf(input.charAt(i++));
enc2 = this._keyStr.indexOf(input.charAt(i++));
enc3 = this._keyStr.indexOf(input.charAt(i++));
enc4 = this._keyStr.indexOf(input.charAt(i++));

chr1 = (enc1 << 2) | (enc2 >> 4);
chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
chr3 = ((enc3 & 3) << 6) | enc4;

output = output + String.fromCharCode(chr1);

if (enc3 != 64) {
output = output + String.fromCharCode(chr2);
}
if (enc4 != 64) {
output = output + String.fromCharCode(chr3);
}

}

output = Base64._utf8_decode(output);

return output;

},

// private method for UTF-8 encoding
_utf8_encode : function (string) {
string = string.replace(/\r\n/g,"\n");
var utftext = "";

for (var n = 0; n < string.length; n++) {

var c = string.charCodeAt(n);

if (c < 128) {
utftext += String.fromCharCode(c);
}
else if((c > 127) && (c < 2048)) {
utftext += String.fromCharCode((c >> 6) | 192);
utftext += String.fromCharCode((c & 63) | 128);
}
else {
utftext += String.fromCharCode((c >> 12) | 224);
utftext += String.fromCharCode(((c >> 6) & 63) | 128);
utftext += String.fromCharCode((c & 63) | 128);
}

}

return utftext;
},

// private method for UTF-8 decoding
_utf8_decode : function (utftext) {
var string = "";
var i = 0;
var c = c1 = c2 = 0;

while ( i < utftext.length ) {

c = utftext.charCodeAt(i);

if (c < 128) {
string += String.fromCharCode(c);
i++;
}
else if((c > 191) && (c < 224)) {
c2 = utftext.charCodeAt(i+1);
string += String.fromCharCode(((c & 31) << 6) | (c2 & 63));
i += 2;
}
else {
c2 = utftext.charCodeAt(i+1);
c3 = utftext.charCodeAt(i+2);
string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63));
i += 3;
}

}

return string;
}

}

上传工具类

上传

/static/js/alioss_upload.js

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
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
import CryptoJS from 'crypto-js';
import {
Base64
} from '@/static/utils/base64.js';

let config = {
accessid: 'LTAxxxxxxxbSCf',
accesskey: 'XwxxxxxxxZhCB',
osshost: 'https://xxxx.oss-cn-qingdao.aliyuncs.com/'
}

export function getOSS() {
let date = new Date()
date = date.setHours(date.getHours() + 1)
let extime = "" + new Date(date).toISOString()
var policyText = {
"expiration": extime, //设置该Policy的失效时间,超过这个失效时间之后,就没有办法通过这个policy上传文件了
"conditions": [
["content-length-range", 0, 1024 * 1024 * 100] // 设置上传文件的大小限制
]
};
let policyBase64 = Base64.encode(JSON.stringify(policyText))
var signature = CryptoJS.HmacSHA1(policyBase64, config.accesskey).toString(CryptoJS.enc.Base64);
var timetamp = new Date().getTime();
return {
host: config.osshost,
accessid: config.accessid,
policyBase64: policyBase64,
signature: signature,
}
}

export function uploadFileToAliOss(path, savePath) {
let OSS = getOSS();
console.log('开始上传')
let self = this;
return new Promise((resolve, reject) => {
uni.uploadFile({
url: OSS.host,
filePath: path,
fileType: 'image',
name: 'file',
formData: {
'key': savePath,
'OSSAccessKeyId': OSS.accessid,
'success_action_status': '200',
'policy': OSS.policyBase64,
'signature': OSS.signature,
},
success: (res) => {
console.log(res.data)
uni.showToast({
title: '上传成功',
icon: 'success',
duration: 1000
});
console.log(OSS.host + savePath)
resolve('suc');
},
fail: (err) => {
console.log('upload fail', err);
uni.showModal({
content: err.errMsg,
showCancel: false
});
reject('err')
}
});
})
}

生成保存位置

引用的生成UUID的库大都不支持小程序。

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
function generateUUID() {
var d = new Date().getTime(); //Timestamp
var d2 = (performance && performance.now && (performance.now() * 1000)) || 0;
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
var r = Math.random() * 16; //random number between 0 and 16
if (d > 0) { //Use timestamp until depleted
r = (d + r) % 16 | 0;
d = Math.floor(d / 16);
} else { //Use microseconds since page-load if supported
r = (d2 + r) % 16 | 0;
d2 = Math.floor(d2 / 16);
}
return (c === 'x' ? r : (r & 0x3 | 0x8)).toString(16);
});
}

function getDateStr() {
const now = new Date()
const year = now.getFullYear()
const month = now.getMonth() + 1
const day = now.getDate()
const formattedMonth = month.toString().padStart(2, '0')
const formattedDay = day.toString().padStart(2, '0')
return `${year}-${formattedMonth}-${formattedDay}`
}

export const getSavePath = (filePath) => {
let fileName = filePath.split('/').pop()
fileName = fileName.replace(/[^a-zA-Z0-9_.-]/g, '')
let uuidStr = generateUUID()
return `cxz_mp/imgs/${getDateStr()}/${uuidStr}-${fileName}`;
}