uniapp / uniCloud 的文件操作
背景
本项目的智能客服模块基于 RAG(检索增强生成)技术,支持知识库管理,为用户提供精准的商品咨询和导购服务。知识库的管理涉及文件操作,需要实现文件上传、读取、下载和删除功能。
提示
本项目使用的云服务空间是阿里云服务空间,因此所有文件操作均以阿里云为基础,未考虑支付宝云、腾讯云。
文件上传
通过组件上传
uni-flie-picker 是 uniapp 官方提供的文件选择组件,用于选择文件并上传,可以选择图片、视频等任意文件并上传到当前绑定的服务空间。
::: 注意事项
如不绑定服务空间,
autoUpload默认为false且不可更改选择文件目前只支持 H5 和 微信小程序平台 :::
<template>
<uni-file-picker
ref="filePicker"
v-model="knowledgeData.files"
file-mediatype="all"
file-extname="txt,doc,docx,md"
dir="knowledge"
:title="fileLimit === 1 ? `请重新上传文件:${currentId}` : `最多选择${fileLimit}个文件`"
:auto-upload="false"
:limit="fileLimit"
@select="onSelectFiles">
</template>
<script>
export default {
data() {
return {
currentId: null,
fileLimit: 9,
knowledgeData:{
files: [],
}
}
},
onLoad(e){
// 更新文件时,获取当前文件 id,文件数量限制为 1
this.currentId = e?.id || null
this.fileLimit = e.id ? 1 : 9
},
methods:{
// 处理文件选择
onSelectFiles(e) {
// 清除之前的选择
this.knowledgeData.files = []
// 存储选择的文件信息
this.knowledgeData.files = e.tempFiles.map((tempFile) => ({
name: tempFile.name,
url: tempFile.url, // 本地文件路径
size: tempFile.size,
content: '',
}))
},
}
}
</script>组件属性:
ref:组件的引用名称,用于获取组件实例v-model:组件的数据绑定,用于获取选择的文件信息mode: 选择文件后文件列表样式,默认为 list,可选值 list、gridfile-mediatype:选择文件类型,默认为 image,可选值 all、image、videofile-extname:选择文件后缀,用逗号分隔多个文件后缀名,如:txt,doc,docx,mddir:上传文件保存的目录名称,默认为"/",即根目录title:选择文件提示信息auto-upload:是否自动上传,默认为 truelimit:选择文件数量限制,默认为 9@select:选择文件事件,这里我们获取本地文件的存储路径等信息,用于后续手动上传文件到云存储。
uni-flie-picker 组件在绑定服务空间后,会自动上传文件,出于用户体验考虑,我们将自动上传关闭,并手动上传文件。
手动上传文件可以通过 ref 调用 uni-flie-picker 组件的 upload 方法。
<script>
await this.$refs.filePicker.upload()
</script>但是存在一个问题,文件上传成功后,文件存储路径并不在 dir 属性指定的 knowledge 目录下,而是在默认上传目录 cloudstorage 下。即:
问题
uni-flie-picker 组件开启自动上传后,
dir属性生效;uni-flie-picker 组件关闭自动上传后,
dir属性不生效。
解决这个问题,我们考虑使用 uniCloud 云存储的 API 进行上传。
通过 uniCloud 云存储上传
根据 uniCloud 官网的描述,内置云存储的上传方式有3种:
web界面:即在https://unicloud.dcloud.net.cn/ web控制台,点击云存储,通过web界面进行文件上传。该管理界面同时提供了资源浏览、删除等操作界面。
客户端API或组件上传:在前端js中编写
uniCloud.uploadFile,或者使用uni ui的FilePicker组件,文件选择 + 上传均封装完毕。云函数上传文件到云存储:即在云函数js中编写
uniCloud.uploadFile
注意
- 前端和云函数端,均有一个相同名称的api:uniCloud.uploadFile。不要混淆。
此外,uniCloud 官网的描述了目录支持:
阿里云在 HBuilderX 3.8.5 及之后版本支持以上传时指定的
cloudPath作为文件路径进行文件存储,需要在上传时指定参数cloudPathAsRealPath: true来启用目录支持;阿里云在
cloudPathAsRealPath为false时传的文件都存储在 cloudstorage 目录下。
通过云函数 API 上传
在云函数中操作云存储文件(不是在前端),包括在云函数里上传、删除云存储文件。
uniCloud.uploadFile(Object uploadFileOptions) 中 uploadFileOptions 参数如下:
cloudPath:上传文件路径,包含文件名称,如:/knowledge/knowledge.txtfileContent:上传文件内容,阿里云支持文件绝对路径或 buffercloudPathAsRealPath:是否以cloudPath作为云端文件绝对路径,默认为false
项目中,我们首先创建一个云函数,云函数内调用 uniCloud.uploadFile,然后前端调用这个云函数,实现文件上传。
但是这样存在一个问题:之前通过组件获取了文件的本地路径,但是通过云函数上传文件时,云函数所在服务器在这个本地路径下找不到文件。
为了解决这个问题,我们可以使用 buffer 上传文件,或者直接使用客户端 API 上传文件。
此外 uniapp 官方也推荐使用客户端 API 上传文件:
如果是从客户端上传文件,一般不建议先把文件从客户端上传到云函数,再由云函数上传到云存储,而是建议客户端直传云存储。
通过客户端 API 上传
uniCloud.uploadFile(Object uploadFileOptions) 用于直接上传文件到云存储。客户端上传文件到云函数、云函数再上传文件到云存储,这样的过程会导致文件流量带宽耗费较大。所以一般上传文件都是客户端直传。
请求参数如下:
cloudPath:上传文件路径,包含文件名称,如:/knowledge/knowledge.txtfilePath:要上传的文件对象cloudPathAsRealPath:是否以cloudPath作为云端文件绝对路径,默认为false
示例:
<script>
let res = await uniCloud.uploadFile({
filePath: file.url,
cloudPath: `knowledge/${file.name}`,
cloudPathAsRealPath: true
})
</script>通过客户端调用该 API 上传文件,上传成功后,文件存储在指定的目录 knowledge 下。其中 filePath 是通过 uni-file-picker 组件的 @select 事件获取到的本地文件路径。
文件读取
文件读取是指获取文件内容、文件存储路径等信息,包括本地文件读取、云存储文件读取。
提示
本项目中,读取文件内容的目的是用于上传知识库。
知识库需要对文件内容进行 MD5 查重、分片、向量化等操作。
本地文件读取
如前所述,本地文件路径的获取可以通过 uni-file-picker 组件的 @select 事件获取。
关键是读取本地文件的内容。
使用 uni.getFileSystemManager 方法可以获取文件管理器。但是 uni.getFileSystemManager 方法仅支持 Harmony OS 系统。
使用 uni.getFileInfo 方法获取文件信息。但是 uni.getFileInfo 方法仅能获取本地文件路径,不能获取文件内容。
最终,我们放弃了本地读取文件内容,而是访问云存储文件以获取文件内容。
云存储文件读取
阿里云直接使用 http 请求 url 地址即可下载文件,无需使用 uniCloud.downloadFile 接口。读取文件内容时,只需将数据类型和响应类型设置为 text 即可。
module.exports = {
async getFile(filename) {
let url = URL_CLOUD_STORAGE_DOWNLOAD + `/knowledge/${filename}`
let res = await uniCloud.request({
url: url,
method: 'GET',
dataType: 'text',
responseType: 'text'
});
// 追加 url
new_res = { ...res }
new_res.data = {
content: res.data,
url: url
}
return new_res
}
}- URL_CLOUD_STORAGE_DOWNLOAD 是本项目的内置云存储下载域名,该域名可访问 服务空间总览 获取。

读取文件时,将数据类型和响应类型设置为 text
在响应体中追加 url 以返回,便于将知识库和文件 url 建立对应关系
文件下载
在 uniapp 客户端 API 中:
- [uni.downloadFile](uni.uploadFile(OBJECT) | uni-app官网) 用于下载文件资源到本地,客户端直接发起一个 HTTP GET 请求,返回文件的本地临时路径。
在 uniCloud 云存储 API 中:
客户端无文件下载的 API
云函数 API 中 uniCloud.downloadFile(Object downloadFileOptions) 下载已上传至云存储的文件到云函数代码空间内,但仅支持腾讯云和支付宝云
阿里云直接使用 http 请求 url 下载即可,无需使用 downloadFile 接口
如前所述我们已经将文件云存储的 url 返回,这里我们直接使用已经封装好的 uni.downloadFile 实现文件下载。
// #ifdef H5
// 新标签页打开链接
window.open(this.currentKnowledge.url, '_blank')
// #endif
// #ifdef MP-WEIXIN
uni.downloadFile({
url: this.currentKnowledge.url,
success: function(downloadRes) {
if (downloadRes.statusCode === 200) {
// 保存文件
uni.saveFile({
tempFilePath: downloadRes.tempFilePath,
success: () => {
uni.showToast({
title: '文件下载成功',
icon: 'success'
});
}
});
}
},
});
// #endifH5 端通过新标签页打开 url 下载文件
微信小程序端通过 uni.downloadFile 接口下载文件
文件删除
在 uniapp 客户端 API 中没有文件删除的 API。
在 uniCloud 云存储 API 中:
客户端 API 中 deleteFile 用于在客户端删除云存储文件。但仅腾讯云支持此API,且使用时,需搭配腾讯云提供的自定义登录和权限设置使用;阿里云、支付宝云不支持此API。
云函数 API 中 uniCloud.deleteFile(Object deleteFileOptions) 用于云函数删除云存储文件。参数 fileList,为要删除的文件 ID 列表,格式为 [filePath1, filePath2, ...]。
注意
删除云存储文件是高危操作,不建议在客户端操作,而建议在云函数中操作。
删除文件推荐在云函数中操作,因此,我们需要创建云函数,将云存储文件删除功能封装为云函数,然后调用该云函数进行文件删除。
module.exports = {
async deleteFile(filename) {
return await uniCloud.deleteFile({
fileList: [`/knowledge/${filename}`]
});
}
}总结
文件上传时:
使用 uni-file-picker 组件进行手动上传时,
dir属性不生效;使用 uni-file-picker 组件进行手动上传时,通过
@select事件获取本地文件信息,包括文件名称、文件路径、文件大小等信息。通过云函数上传文件时,直接传递本地文件路径,云函数所在服务器无法找到文件。
通过客户端 API 上传文件时,
cloudPathAsRealPath设置为true,filePath为本地文件路径。
读取文件内容时:
通过访问云存储文件 url 获取文件内容;
阿里云直接使用 http 请求 url 地址即可下载文件;
http 请求读取文件内容时,只需将数据类型和响应类型设置为 text 即可。
下载文件时:
H5 端通过新标签页打开 url 获取文件;
微信小程序端通过 uni.downloadFile 获取文件。
删除文件时:
删除云存储文件是高危操作,建议在云函数中操作;
创建云函数,将云存储文件删除功能封装为云函数,然后调用该云函数进行文件删除。