一、文件下载
① 网络请求配置
- 下载在线文件,需要访问网络,因此需要在 config.json 中添加网络权限:
{
"module": {
"requestPermissions": [
{
"name": "ohos.permission.INTERNET",
"reason": "下载文件需要网络访问"
},
{
"name": "ohos.permission.READ_MEDIA",
"reason": "访问下载文件"
},
{
"name": "ohos.permission.WRITE_MEDIA",
"reason": "保存下载文件"
}
]
}
}
② 文件下载核心代码
import http from '@ohos.net.http';
let httpRequest = http.createHttp()
let opt: http.HttpRequestOptions = {
method: http.RequestMethod.GET,
header: { 'Accept-Encoding': 'gzip, deflate, br',
'Accept-Language': 'zh-CN,zh;q=0.9',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
},
expectDataType: http.HttpDataType.ARRAY_BUFFER
}
httpRequest.request(url, opt)
.then((resp) => {
let respDisp: string = resp.header["content-disposition"]
// 从文件信息提取文件名称,包含双引号
let fileNameWithQuote = respDisp.split(";")[1].split("=")[1]
// 去掉文件名称的双引号
let fileName = fileNameWithQuote.substring(1, fileNameWithQuote.length - 1)
// 保存文件到本地
let filePath = this.saveFile(resp.result as ArrayBuffer, fileName)
if (filePath.length > 0) {
let msgHistory = "文件已保存到:" + filePath + "\r\n"
toast('下载成功,可在“我的下载”中查看下载的附件')
} else {
toast('保存失败')
}
})
.catch((e:Error) => {
toast('下载失败')
})
③ 保存文件到本地
import fs from '@ohos.file.fs'
// 保存文件并返回保存路径
saveFile(buf: ArrayBuffer, fileName: string): string {
let pathDir = getContext(this).filesDir
let filePath = pathDir + "/" + fileName
let file = fs.openSync(filePath, fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE)
fs.writeSync(file.fd, buf)
fs.closeSync(file)
return filePath
}
二、文件的读取
- 在上面的文件被下载后,我们已经将文件保存到我们的本地路径中,那么该如何读取这些文件呢?
import fs from '@ohos.file.fs'
@State filesArray : MyDownloadDataModel[] = []
getListFile(): void {
// 获取当前应用的文件目录路径
let pathDir = getContext(this).filesDir
// 配置文件列表的筛选选项
let listFileOption: ListFileOptions = {
recursion: false, // 不递归子目录
listNum: 0, // 0表示获取所有符合条件的文件
filter: {
suffix: [".pdf"], // 只筛选这些后缀的文件
fileSizeOver: 0 // 文件大小超过0字节(即所有大小)
}
};
// 同步获取目录下符合条件的文件列表
let files = fs.listFileSync(pathDir, listFileOption);
// 遍历所有匹配的文件
for (let i = 0; i < files.length; i++) {
// 拼接文件的完整路径
let filePath = pathDir + '/' + files[i]
// 创建文件数据模型对象
let model = new MyDownloadDataModel
model.fileName = files[i]
model.filePath = filePath
model.fileType = files[i].split('.')[1]
// 异步获取文件大小信息
fs.stat(filePath).then((stat: fs.Stat) => {
// 计算文件大小(转换为MB)
let size = stat.size / 1024 / 1024
// 设置文件大小字符串表示(示例中实际用的是KB单位)
model.fileSize = stat.size.toString() + "KB"
}).catch((err: BusinessError) => {
// 如果获取大小失败,设置默认值0
model.fileSize = "0 KB"
});
// 异步获取文件创建时间信息
fs.lstat(filePath).then((stat: fs.Stat) => {
// 将时间戳转换为可读格式
model.fileCreateTime = DateRsUtil.getDateStringWithTimeStamp(stat.ctime)
// 将完整模型对象加入数组
this.filesArray.push(model)
}).catch((err: BusinessError) => {
model.fileCreateTime = ''
this.filesArray.push(model)
});
}
}
三、预览文件
① 使用系统预览能力(推荐)
// 获取当前UI上下文对象(用于界面相关操作)
let uiContext = getContext(this);
// 配置预览窗口的显示参数
let displayInfo: filePreview.DisplayInfo = {
x: 100, // 预览窗口左上角的x坐标(单位:像素)
y: 100, // 预览窗口左上角的y坐标(单位:像素)
width: 800, // 预览窗口的宽度(单位:像素)
height: 800 // 预览窗口的高度(单位:像素)
};
// 配置要预览的文件信息
let fileInfo: filePreview.PreviewInfo = {
title: file.fileName, // 设置预览窗口标题为文件名
uri: fileUri.getUriFromPath(file.filePath), // 将文件路径转换为URI格式
mimeType: 'application/pdf' // 明确指定文件类型为PDF
};
// 调用文件预览功能
filePreview.openPreview(uiContext, fileInfo, displayInfo)
.then(() => {
// 预览成功回调
console.info('Succeeded in opening preview');
})
.catch((err: BusinessError) => {
// 预览失败回调
console.error(
`Failed to open preview,
err.code = ${err.code}, // 错误码
err.message = ${err.message}` // 错误描述
);
});
② Web组件预览(备选)
import webview from '@ohos.web.webview';
@Component
struct PdfWebView {
controller: webview.WebviewController = new webview.WebviewController();
build() {
Column() {
Web({
src: $rawfile('downloaded.pdf'), // 或使用file://路径
controller: this.controller
})
.onPageEnd(e => {
console.info('PDF加载完成');
})
}
}
}
四、分享文件
requestShareFile(file: MyDownloadDataModel) {
// 构造ShareData,需配置一条有效数据信息
let data: systemShare.SharedData = new systemShare.SharedData({
utd: utd.UniformDataType.PDF,
content: file.fileName
});
let uiContext = getContext(this)
// 获取文件的沙箱路径
let pathInSandbox = uiContext.filesDir + file.filePath
// 将沙箱路径转换为uri
let uri = fileUri.getUriFromPath(pathInSandbox)
// 添加多条记录
data.addRecord({
utd: utd.UniformDataType.PDF,
uri: uri
});
// 构建ShareController
let controller: systemShare.ShareController = new systemShare.ShareController(data)
// 注册分享面板关闭监听
controller.on('dismiss', () => {
// 分享结束,可处理其他业务
});
// 进行分享面板显示
let context = getContext(this) as common.UIAbilityContext;
controller.show(context, {
previewMode: systemShare.SharePreviewMode.DETAIL,
selectionMode: systemShare.SelectionMode.SINGLE
});
}
五、高级功能扩展
① 断点续传
const downloadRange = (url: string, filePath: string, start: number) => {
const file = fileio.openSync(filePath, fileio.O_RDWR);
fileio.lseekSync(file.fd, start, fileio.SeekType.SEEK_SET);
return http.request(url, {
header: { 'Range': `bytes=${start}-` },
file: file.fd
});
};
② 文件缓存管理
function cleanOldPdfs(maxAge: number = 7 * 24 * 3600 * 1000) {
const dir = fileio.opendirSync('internal://cache/');
const now = Date.now();
for (const entry of dir.readSync()) {
if (entry.name.endsWith('.pdf')) {
const stat = fileio.statSync(`internal://cache/${entry.name}`);
if (now - stat.mtime * 1000 > maxAge) {
fileio.unlinkSync(`internal://cache/${entry.name}`);
}
}
}
}
六、注意事项
① 安全性
- 验证下载URL的合法性;
- 检查文件签名(如有);
- 敏感文件建议加密存储。
② 性能优化
- 大文件分块下载(如 10MB/块);
- 预览前校验文件头是否为%PDF;
③ 兼容性处理
function tryOpenWithWebView(filePath: string) {
try {
// 尝试转换为content:// URI
const uri = fileuri.getUriFromPath(filePath, 'content');
// 调用系统文件选择器
// ...
} catch (error) {
prompt.showToast({ message: '无法打开PDF文件' });
}
}