uniapp开发选取文件,前端直传阿里云。
·
本文主要解决前端使用uniapp开发app时需要文件上传所遇到的问题,刷到就是赚到。
1.选取文件
其实正常来说这根本就不能算是一个问题,因为uniapp的api支持。现在不支持文件选取了,具体可以看文档uni.chooseFile。但是话又说回来还是现在uniapp还是支持正常的图片上传视频上传的。
直接上代码,这是我苦苦找了很久才找到的。注意仅仅是app
plus.io.chooseFile({
title: 'x',
multiple: false,
}, (res) => {
console.error(res);
})
2.直传阿里云
这个才是困扰我很长时间的问题,刚开始尝试下载了ali-oss让后发现没法用。也并不是没法用主要是阿里云的芬片上传和直传我只找到了使用file文件上传的方法,并没有说可以用临时路径上传,后续又开始找各种方法把临时路径转为file文件尝试上传但是一直不行一直不行!!!真是一步一个坑,解决一个问题就会有下一个问题。
1.前端生成Policy和Signature方法
// 生成Policy(前端动态生成,替换接口返回的Policy)
function generatePolicy(options) {
const {
bucket,
expire = 3600,
allowPrefix = '*',
allowMimeTypes = ['']
} = options;
const expiration = new Date();
expiration.setTime(expiration.getTime() + expire * 1000);
const expirationISO = expiration.toISOString();
// 关键修改:仅保留必要条件,删除所有与$method相关的限制
const policyConditions = [
['eq', '$bucket', bucket], // 限制bucket
['starts-with', '$key', allowPrefix], // 限制上传路径
...allowMimeTypes.map(type => ['starts-with', '$Content-Type', type]) // 限制文件类型
// 移除所有["eq", "$method", "..."]条件
];
const policyObject = { expiration: expirationISO, conditions: policyConditions };
return CryptoJS.enc.Base64.stringify(
CryptoJS.enc.Utf8.parse(JSON.stringify(policyObject))
);
}
// 生成Signature(前端动态生成,替换接口返回的Signature)
function generateSignature(policy, accessKeySecret) {
const hmac = CryptoJS.HmacSHA1(policy, accessKeySecret);
return CryptoJS.enc.Base64.stringify(hmac);
}
注意:需要下载crypto-js
2.完整代码临时token从后端接口拿
上代码创建uploadOss.js
import store from '@/store';
import { getOsstoken } from '@/api/index.js';
import CryptoJS from 'crypto-js';
// 获取文件大小
function getfileSize(val) {
if (val === 0) return "0 B";
const k = 1024;
const sizes = ["B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"];
const i = Math.floor(Math.log(val) / Math.log(k));
return (val / Math.pow(k, i)).toFixed(2) + " " + sizes[i];
}
// 时间格式转换
function parseTime(time, pattern) {
if (arguments.length === 0 || !time) return null;
const format = pattern || '{y}-{m}-{d} {h}:{i}:{s}';
let date;
if (typeof time === 'object') {
date = time;
} else {
if (typeof time === 'string' && /^[0-9]+$/.test(time)) {
time = parseInt(time);
} else if (typeof time === 'string') {
time = time.replace(/-/gm, '/').replace('T', ' ').replace(/\.[\d]{3}/gm, '');
}
if (typeof time === 'number' && time.toString().length === 10) {
time *= 1000;
}
date = new Date(time);
}
const formatObj = {
y: date.getFullYear(),
m: date.getMonth() + 1,
d: date.getDate(),
h: date.getHours(),
i: date.getMinutes(),
s: date.getSeconds(),
a: date.getDay()
};
return format.replace(/{(y|m|d|h|i|s|a)+}/g, (result, key) => {
let value = formatObj[key];
if (key === 'a') return ['日', '一', '二', '三', '四', '五', '六'][value];
return result.length > 0 && value < 10 ? `0${value}` : value || 0;
});
}
// 生成Policy(前端动态生成,替换接口返回的Policy)
function generatePolicy(options) {
const {
bucket,
expire = 3600,
allowPrefix = '*',
allowMimeTypes = ['']
} = options;
const expiration = new Date();
expiration.setTime(expiration.getTime() + expire * 1000);
const expirationISO = expiration.toISOString();
// 关键修改:仅保留必要条件,删除所有与$method相关的限制
const policyConditions = [
['eq', '$bucket', bucket], // 限制bucket
['starts-with', '$key', allowPrefix], // 限制上传路径
...allowMimeTypes.map(type => ['starts-with', '$Content-Type', type]) // 限制文件类型
// 移除所有["eq", "$method", "..."]条件
];
const policyObject = { expiration: expirationISO, conditions: policyConditions };
return CryptoJS.enc.Base64.stringify(
CryptoJS.enc.Utf8.parse(JSON.stringify(policyObject))
);
}
// 生成Signature(前端动态生成,替换接口返回的Signature)
function generateSignature(policy, accessKeySecret) {
const hmac = CryptoJS.HmacSHA1(policy, accessKeySecret);
return CryptoJS.enc.Base64.stringify(hmac);
}
// 缓存管理对象
const tokenCache = {
token: null,
expireTime: 0,
tokenPromise: null,
};
// 获取OSS临时凭证(带缓存)
async function getTokenWithCache() {
const now = Date.now();
if (tokenCache.token && now < tokenCache.expireTime) {
return tokenCache.token;
}
if (tokenCache.tokenPromise) {
return tokenCache.tokenPromise;
}
tokenCache.tokenPromise = getOsstoken()
.then(res => {
if (res.code === 200) {
tokenCache.token = res.data;
tokenCache.expireTime = now + 50 * 60 * 1000; // 50分钟缓存
return res.data;
}
throw new Error('获取OSS凭证失败');
})
.finally(() => {
tokenCache.tokenPromise = null;
});
return tokenCache.tokenPromise;
}
// 生成随机文件名
function generateFileName(originalName) {
const ext = originalName.substring(originalName.lastIndexOf('.')) || '';
const randomStr = Math.random().toString(36).substring(2, 10);
return `${Date.now()}_${randomStr}${ext}`;
}
// 统一文件上传方法(核心修改:前端生成Policy和Signature)
async function uploadFileToOSS(filePath, originalFileName, isMultipart = false, onProgress = () => {}) {
try {
const tokenData = await getTokenWithCache();
console.log('OSS临时凭证:', tokenData);
// 1. 生成OSS存储路径
const datePath = parseTime(new Date(), '{y}/{m}/{d}/');
const fileName = generateFileName(originalFileName || 'file');
const ossPath = `lawyer/${datePath}${fileName}`; // 上传到lawyer目录
// 2. 前端动态生成Policy(关键:替换接口返回的Policy)
const policy = generatePolicy({
bucket: tokenData.bucket,
expire: 3600, // 与缓存时间保持一致
allowPrefix: 'lawyer/' // 限制仅能上传到lawyer前缀路径
});
// 3. 前端动态生成Signature(关键:替换接口返回的Signature)
const signature = generateSignature(policy, tokenData.accessKeySecret);
// 4. 构建上传URL和表单数据
const uploadUrl = `https://${tokenData.bucket}.${tokenData.region}.aliyuncs.com`;
const formData = {
'key': ossPath, // 上传路径
'OSSAccessKeyId': tokenData.accessKeyId, // 临时AK
'policy': policy, // 前端生成的Policy
'Signature': signature, // 前端生成的Signature
'x-oss-security-token': tokenData.stsToken, // STS Token(必须)
'success_action_status': '200' // 成功状态码
};
// 5. 使用uni.uploadFile上传
return new Promise((resolve, reject) => {
const uploadTask = uni.uploadFile({
url: uploadUrl,
filePath: filePath, // 本地文件路径
name: 'file', // 表单字段名固定为file
formData: formData,
method: 'POST',
success: (res) => {
console.log('上传响应:', res);
if (res.statusCode === 200) {
resolve({
url: `https://${tokenData.bucket}.${tokenData.region}.aliyuncs.com/${ossPath}`,
ossPath: ossPath,
status: 'success'
});
} else {
reject(new Error(`上传失败,状态码: ${res.statusCode},响应: ${res.data}`));
}
},
fail: (err) => {
console.error('上传失败:', err);
reject(err);
}
});
// 监听进度
uploadTask.onProgressUpdate((progress) => {
console.log(progress,'-----');
onProgress(progress.progress); // 转为0-1的进度值
});
});
} catch (error) {
console.error('OSS上传错误:', error);
throw error;
}
}
// 获取文件Base64(适配Uniapp)
function getBase64(filePath) {
return new Promise((resolve, reject) => {
uni.getFileSystemManager().readFile({
filePath: filePath,
encoding: 'base64',
success: (res) => {
resolve(`data:image/jpeg;base64,${res.data}`);
},
fail: (err) => {
reject(err);
}
});
});
}
// 对外暴露API
export default {
uploadFile(filePath, originalFileName, onProgress) {
return uploadFileToOSS(filePath, originalFileName, false, onProgress);
},
uploadLargeFile(filePath, originalFileName, onProgress) {
return uploadFileToOSS(filePath, originalFileName, true, onProgress);
},
getfileSize,
getBase64,
clearTokenCache() {
tokenCache.token = null;
tokenCache.expireTime = 0;
tokenCache.tokenPromise = null;
}
};
3.页面中使用
正常使用上传图片
async uploadImage() {
try {
// 直接选择图片
const res = await uni.chooseImage({
count: 1,
sourceType: ['album', 'camera']
});
let originalFileName = res.tempFilePaths[0].substring(res.tempFilePaths[0].lastIndexOf("/") + 1)
oss.uploadFile(res.tempFilePaths[0], originalFileName, (progress) => {
console.log('上传进度:', progress);
}).then(result => {
console.log('上传成功:', result.url);
editUser({
avatar: result.url
}).then(res => {
if (res.code == 200) {
uni.showToast({
title: '头像设置成功',
icon: 'none'
})
this.info()
}
})
}).catch(error => {
console.error('上传失败:', error);
});
} catch (e) {
console.error('图片上传失败:', e);
}
},
上传文件
uploadFiless() {
plus.io.chooseFile({
title: 'x',
multiple: false,
}, (res) => {
let originalFileName = res.files[0].substring(res.files[0].lastIndexOf("/") + 1)
oss.uploadFile(res.files[0], originalFileName, (progress) => {
console.log('上传进度:', progress);
this.pgList = progress
}).then(result => {
console.log('上传成功:', result.url);
this.videoUrl = result.url
}).catch(error => {
console.error('上传失败:', error);
});
})
},
更多推荐
所有评论(0)