
鸿蒙技术分享:鸿蒙应用开发:文件下载、选择、打开
·
鸿蒙应用开发:文件下载、选择、打开
文件下载
项目业务需求,一般会有将网络文件下载到沙盒或者公共目录的场景。而文件下载的第一步,是获取下载文件的目标地址选择。
按照官方文档的建议,代码如下:
import { BusinessError, Callback } from '@ohos.base'
import { fileIo, fileUri } from '@kit.CoreFileKit';
import picker from '@ohos.file.picker';
import fs from '@ohos.file.fs';
import { FWHttp, NetResponse } from '@fw/net';
export interface FileGetDownloadPathOptionsExtra {
/**
* 1:表示使用公共目录 0:App沙盒空间
*/
external?: number
/**
* true在公共目录下面新建一个appid对应的文件
*/
bind?: boolean
/**
* true重复下载某个文件不做覆盖替换,而是添加文件顺序号共存。
* bind==false时不支持该参数,因为公共目录是使用picker访问,bind==false时picker会控制文件名是否重复。
*/
repeat?: boolean
}
/**
* 文件下载参数
*/
export interface FileGetDownloadPathOptions {
/**
* 网络下载路径
*/
downloadUrl: string
/**
* 文件名称
*
* 若为undefined,则取downloadUrl最后一段path为文件名
*/
fileName?: string
// /**
// * 信任主机
// */
// trustAllHosts?: string
/**
* 成功回调
*/
successCallback?: Callback<Record<string, string>>
/**
* 失败回调
*/
failCallback?: Callback<string>
/**
* 扩展参数
*/
extra?: FileGetDownloadPathOptionsExtra
}
export class File {
static getUrlFileName(url: string): string {
if (!url) {
return'';
}
let index = url.lastIndexOf('/');
if (index === -1) {
return'';
}
url = url.substring(index + 1);
index = url.indexOf('?');
if (index !== -1) {
url = url.substring(0, index);
}
return url;
}
static getNoneExistFileName(path: string, fileName: string): string {
let newFileName = fileName;
let filePath = path + "/" + newFileName;
let fileExist = fileIo.accessSync(filePath);
let num = 1;
while (fileExist) {
if (fileName.indexOf('.') > -1) {
let index = fileName.lastIndexOf('.');
newFileName = fileName.substring(0, index) + "_" + num + "." + fileName.substring(index + 1);
} else {
newFileName = fileName + "_" + num;
}
filePath = path + "/" + newFileName;
fileExist = fileIo.accessSync(filePath);
num++;
}
return filePath;
}
static async getDownloadPath(options: FileGetDownloadPathOptions) {
try {
let DOWNLOAD_TO_PATH = FWFileUtil.getFilesDirPath(`download`); // 默认取沙盒文件路径
let filename = options.fileName ?? ''
if (options.fileName == undefined) {
filename = File.getUrlFileName(options.downloadUrl)
}
if (options.extra?.external === 1) {
// 获取目录
const documentSaveOptions = new picker.DocumentSaveOptions(); // 创建文件管理器选项实例
if (options.extra?.bind) {
documentSaveOptions.pickerMode = picker.DocumentPickerMode.DOWNLOAD
} else {
documentSaveOptions.newFileNames = [filename]; // 保存文件名
}
let uriString = ''
const documentViewPicker = new picker.DocumentViewPicker(); // 创建文件选择器实例
let documentSaveResult = await documentViewPicker.save(documentSaveOptions)
let uri = new fileUri.FileUri(documentSaveResult[0]);
if (options.extra?.bind) {
if (options.extra?.repeat) {
uriString = File.getNoneExistFileName(uri.path, filename)
} else {
uriString = uri.path + '/' + filename;
}
} else {
uriString = uri.path
}
DOWNLOAD_TO_PATH = uriString
} else {
if (options.extra?.repeat) {
DOWNLOAD_TO_PATH = File.getNoneExistFileName(DOWNLOAD_TO_PATH, filename)
} else {
DOWNLOAD_TO_PATH = FWFileUtil.getFilesDirPath(DOWNLOAD_TO_PATH, filename)
if (fileIo.accessSync(DOWNLOAD_TO_PATH)) {
fileIo.unlinkSync(DOWNLOAD_TO_PATH);
}
}
}
if (options.successCallback != undefined) {
options.successCallback({nativeURL: DOWNLOAD_TO_PATH})
}
// const res: NetResponse<ArrayBuffer> = await FWHttp.getInstance().download(options.downloadUrl)
// let file = fs.openSync(DOWNLOAD_TO_PATH, fs.OpenMode.WRITE_ONLY | fs.OpenMode.CREATE)
// fs.writeSync(file.fd, res.result)
//
// if (options.successCallback != undefined) {
// options.successCallback({nativeURL: DOWNLOAD_TO_PATH})
// }
} catch (e) {
if (options.failCallback != undefined) {
options.failCallback('获取下载路径失败,原因:' + e.message)
}
}
}
static readonly separator: string = '/';
/**
* 获取文件目录下的文件夹路径或文件路径。
* @param dirPath 文件路径,支持完整路径 和 相对路径(download/wps/doc)。dirPath传空表示根目录
* @param fileName 文件名(test.text)
* @returns
*/
static getFilesDirPath(dirPath: string, fileName?: string): string {
let filePath = getContext().filesDir; //根目录
if (dirPath) {
if (dirPath.startsWith(filePath)) { //路径中包含根目录,是完整路径。
filePath = dirPath;
} else { //路径中不包含根目录,拼接成完整路径。
filePath = filePath + File.separator + dirPath;
}
if (!File.accessSync(filePath)) {
File.mkdirSync(filePath) //如果文件夹不存在就创建
}
}
if (fileName) {
filePath = filePath + File.separator + fileName;
}
return filePath;
}
/**
* 检查文件是否存在,以同步方法。
* @param path 文件应用沙箱路径。
* @returns
*/
static accessSync(path: string): boolean {
return fs.accessSync(path);
}
/**
* 创建目录以同步方法,当recursion指定为true,可多层级创建目录。
* @param path 目录的应用沙箱路径。
* @param recursion 是否多层级创建目录。recursion指定为true时,可多层级创建目录。recursion指定为false时,仅可创建单层目录。
*/
static mkdirSync(path: string, recursion: boolean = true) {
if (recursion) {
fs.mkdirSync(path, recursion);
} else {
fs.mkdirSync(path);
}
}
}
- 获取沙盒路径不涉及隐私问题,权限问题,所以没有什么特殊点;
- 核心在于获取公共目录地址;按照官方文档,要使用
DocumentViewPicker.save()
方法,而其返回值是FileUri格式的字符串;例如:file://docs/storage/Users/currentUser/Download/component-Library.zip
; - 而
@ohos.file.fs
库中,大部分方法都是要求传入path
格式的字符串;因此需要使用FileUri
类进行解析后获得path
;例如:/storage/Users/currentUser/Download/component-Library.zip
; - 具体的下载逻辑一般依赖项目中集成的网络框架,这里暂不包含具体下载逻辑,通过callback回传下载地址url;
文件选择
项目业务需求中,有选择文件的需求。
选择文件,系统提供了DocumentViewPicker.select()
方法。
import { fileUri, picker } from '@kit.CoreFileKit'
export interface FileChooserOpenOptions {
defaultFilePathUri?: string
success: Callback<Record<string, string>>
failure: Callback<string>
}
export class FileChooser {
static async open(options: FileChooserOpenOptions) {
try {
const documentViewPicker = new picker.DocumentViewPicker(); // 创建文件选择器实例
const documentSelectOptions = new picker.DocumentSelectOptions(); // 创建文件管理器选项实例
documentSelectOptions.defaultFilePathUri = options.defaultFilePathUri
let documentSelectResult = await documentViewPicker.select(documentSelectOptions)
if (documentSelectResult.length > 0) {
let uri = new fileUri.FileUri(documentSelectResult[0]);
if (options.success != undefined) {
options.success({
'path': uri.path,
'name': uri.name
})
}
} else {
if (options.failure != undefined) {
options.failure(`未选择文件`)
}
}
} catch (e) {
if (options.failure != undefined) {
options.failure(e.message)
}
}
}
}
DocumentSelectOptions
中有一个defaultFilePathUri
参数,该参数支持沙盒路径;
因此当传入沙盒路径uri时,就可以在系统弹窗中选择指定沙盒路径下的文件;
不传入该参数时,默认是选择公共目录下的文件。
需要注意的是,该参数要求是uri格式的。
文件打开
项目业务需求中,有将文件使用外部应用打开的需求,比如Word/Excel/pdf文件使用外部应用打开等场景。
import { fileUri } from '@kit.CoreFileKit';
import { common, Want, wantConstant } from '@kit.AbilityKit';
import { BusinessError } from '@kit.BasicServicesKit';
export interface FileOpener2OpenOptionsCallbackContext {
/**
* 成功回调
*/
success?: Callback<string>
/**
* 失败回调
*/
error?: Callback<string>
}
export interface FileOpener2OpenOptions {
fileName: string
contentType: string
callbackContext: FileOpener2OpenOptionsCallbackContext
}
export class FileOpener2 {
static open(context: common.UIAbilityContext, options: FileOpener2OpenOptions) {
// 获取文件沙箱路径
let filePath = options.fileName;
// 将沙箱路径转换为uri
let uri = fileUri.getUriFromPath(filePath);
let want: Want = {
// 配置被分享文件的读写权限,例如对被分享应用进行读写授权
flags: wantConstant.Flags.FLAG_AUTH_WRITE_URI_PERMISSION | wantConstant.Flags.FLAG_AUTH_READ_URI_PERMISSION,
// 配置分享应用的隐式拉起规则
action: 'ohos.want.action.sendData',
uri: uri,
type: options.contentType
}
context.startAbility(want)
.then(() => {
console.info('Invoke getCurrentBundleStats succeeded.');
})
.catch((err: BusinessError) => {
console.error(`Invoke startAbility failed, code is ${err.code}, message is ${err.message}`);
});
}
}
根据官方文档,打开第三方应用需要使用context.startAbility()
方法。
其参数为Want
类型:
{
// 配置被分享文件的读写权限,例如对被分享应用进行读写授权
flags: wantConstant.Flags.FLAG_AUTH_WRITE_URI_PERMISSION | wantConstant.Flags.FLAG_AUTH_READ_URI_PERMISSION,
// 配置分享应用的隐式拉起规则
action: 'ohos.want.action.sendData',
uri: uri,
type: options.contentType
}
其中,uri和type分别是文件地址uri字符串和contentType,都需要传,否则调用第三方打开会失败。
更多推荐
所有评论(0)