HarmonyOS鸿蒙Next中在导出文件时,如果空间不足怎么停止导出?
HarmonyOS鸿蒙Next中在导出文件时,如果空间不足怎么停止导出? 【问题描述】:从沙箱目录通过文件保存控件导出文件时,在用户选择保存路径后,系统会生成一个临时文件,此时如果检测到剩余空间不足,想要停止导出怎么办??因为系统生成的文件貌似没有权限删除,如果此时停止导出操作,会在原目录存在一个0字节的文件,但是在用户选择目录前,好像又无法检测剩余空间,因为还不知道用户要保存到哪里
尊敬的开发者,您好,
-
保存到我的云盘时,实际还是存储在手机本地,所以可以使用statfs.getFreeSize判断本地剩余空间。
-
保存到外置存储设备,当前无接口供三方应用获取外置存储设备的剩余空间,如果您希望提供相关功能,请提供以下信息:
请问您是在什么样的业务场景中使用该能力,交互流程是怎样的,方便说明能力不满足可能带来的影响:什么时间用到?是否高频?有无三方库可以做到?若提供该能力,是否会造成大工作量返工?请您注意提供的内容不要包含您或第三方的非公开信息,如给您带来不便,敬请谅解。
更多关于HarmonyOS鸿蒙Next中在导出文件时,如果空间不足怎么停止导出?的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html
- 生成那个0字节的文件应该是保存失败,在复制前创建的。
- 如果空间不足,可以复制保存前检查下,用statfs.getFreeSizeSync。
- 如果复制保存失败,想干掉那个创建的文件,用fileManagerService.deleteToTrash
参考:
saveSandboxFile(srcPath: string, name: string) {
const documentSaveOptions = new picker.DocumentSaveOptions();
documentSaveOptions.newFileNames = [name];
let context = this.getUIContext().getHostContext() as common.UIAbilityContext;
const documentViewPicker = new picker.DocumentViewPicker(context);
documentViewPicker.save(documentSaveOptions).then((documentSaveResult: string[]) => {
this.copyFile(srcPath, documentSaveResult[0])
}).catch((err: BusinessError) => {
console.error(`Invoke documentViewPicker.save failed, code is ${err.code}, message is ${err.message}`);
});
}
copyFile(srcPath: string, targetFile: string): boolean {
try {
let freeSize = statfs.getFreeSizeSync(targetFile);
if (freeSize < fileIo.statSync(srcPath).size) {
return false;
}
let srcFile = fileIo.openSync(srcPath);
let destFile = fileIo.openSync(targetFile, fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE)
fileIo.copyFile(srcFile.fd, destFile.fd).then(() => {
//TODO 复制成功
}).catch(() => {
fileManagerService.deleteToTrash(targetFile).catch(() => {
// TODO: Implement error handling.
});
return false;
}).finally(() => {
fileIo.closeSync(srcFile.fd);
fileIo.closeSync(destFile.fd);
})
} catch (error) {
return false;
}
return true;
}
开发者您好,文件管理服务File Manager Service Kit提供了deleteToTrash删除文件到回收站接口,这边如果在调用保存接口因为内存不足产生了0字节的文件,可以判断不满足后直接删除该文件路径,看下这个方式是否可以满足您的需求?
参考代码:
import { picker, storageStatistics } from '@kit.CoreFileKit';
import { fileManagerService } from '@kit.FileManagerServiceKit';
import fs from '@ohos.file.fs';
@Entry
@Component
struct Index {
build() {
Column() {
Button('保存文件到公共目录模拟内存满的情况下并删除').onClick(async () => {
const documentSaveOptions = new picker.DocumentSaveOptions(); // 创建文件管理器选项实例
documentSaveOptions.newFileNames = ["test.db"]; // 保存文件名(可选)
documentSaveOptions.fileSuffixChoices = ['']; // 保存文件类型(可选)
let uris: Array<string> = [];
const documentViewPicker = new picker.DocumentViewPicker(); // 创建文件选择器实例
documentViewPicker.save(documentSaveOptions).then((documentSaveResult: Array<string>) => {
uris = documentSaveResult;
let context = this.getUIContext().getHostContext();
let fileName: string = 'xxxx'; //
//判断剩余存储空间大小
try {
let number = storageStatistics.getFreeSizeSync();
if (number > 0) {
fileManagerService.deleteToTrash(uris[0]);
} else {
context?.resourceManager.getRawFileContent(fileName, (error, value) => {
if (error) {
console.log(`rawfile文件保存到用户目录 error Code: ${error.code} error Msg: ${error.message}`);
return;
}
// 这里读取到文件内容可以进行下一步操作
let myBuffer: ArrayBufferLike = value.buffer;
console.info('documentViewPicker.save to file succeed and uris are:' + uris);
let uri = uris[0];
let file = fs.openSync(uri, fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE);
let writeLen2 = fs.writeSync(file.fd, myBuffer);
fs.closeSync(file);
});
}
console.info(`getFreeSizeSync successfully, number is ${number}`);
} catch (err) {
let error: BusinessError = err as BusinessError;
console.error(`getFreeSizeSync failed with error, code is ${error.code}, message is ${error.message}`);
}
}).catch((err: BusinessError) => {
console.error(`Invoke documentViewPicker.save failed, code is ${err.code}, message is ${err.message}`);
})
})
}
.height('100%')
.width('100%')
}
}
现在用的就是这个方案,一般可以检测手机内置空间,但是如果在用户选择目录前判断的话,如果用户选择的保存到云盘或者外接存储就没用了,所以想看看又没有新的优化方案
这个场景下,比较稳妥的处理方式是:不要等系统创建目标文件后再判断空间,而是导出前先预估文件大小。
因为 FilePicker 返回用户选定 URI 后,系统确实可能已经创建目标文件。
如果这时再发现空间不足直接中断,往往会留下 0 字节占位文件,而且应用侧通常没有权限直接删掉这个由系统创建的目标文件。
建议这样处理:
方案1(推荐):导出前预估大小
如果能提前拿到导出文件大小(或压缩后预估值),在拉起保存前先判断:
- 源文件大小
- 编码/压缩后的预计大小
- 预留一定安全余量(比如 +10%)
空间不足直接提示,不进入保存流程。
方案2:分块写入 + 捕获写入异常
执行写入时监听异常:
try {
// write
} catch (err) {
// ENOSPC
}
捕获到空间不足(ENOSPC / no space left)后:
- 立即停止写入
- 提示“存储空间不足,导出失败”
但残留 0 字节文件通常只能接受,交给用户手动删除。
方案3:先导出到应用沙箱,再让用户另存
流程:
沙箱生成完整文件
→ 校验大小成功
→ 再调用保存/分享
这样至少能确保不会在目标目录留下空文件。
一句话:
HarmonyOS 当前这个流程下,用户选路径后系统创建的目标文件通常不能由应用主动删除;要避免 0 字节文件,核心是“导出前预估空间”,而不是失败后回滚删除。
用户通过保存控件选择路径前,应用确实不知道最终目标卷和目录,所以很难提前精确判断“目标位置剩余空间”。更稳妥的做法是把流程拆成两段:先在应用沙箱内生成或预估待导出文件大小,再拉起保存控件让用户选择目标位置,拿到 URI 后按块写入。
空间判断上,可以用 file.statvfs/statfs 类能力检查你可访问路径的剩余空间;但对用户选择的外部 URI,不建议按普通路径思路处理,也不要尝试删除系统/文件管理器生成的临时 0 字节文件,因为那不是应用直接拥有的普通沙箱文件。
实际实现建议:导出前先提示预计文件大小;写入时分块写并捕获写入失败/空间不足错误;失败后给用户明确提示“目标位置空间不足,请更换目录或清理空间后重试”。如果业务强依赖“失败不留下 0 字节文件”,需要看 FilePicker/文件管理器当前实现是否支持事务式保存;应用侧通常只能避免继续写入,不能保证删除用户目标目录里的占位文件。
能不能考虑,在目录检测后尝试删除或者引导用户手动清理呢?
比如拿到目录URI后。立即查该文件所在目录剩余空间。
如果空间不足:
尝试 fs.unlink(uri),权限足够应该可删。
如果删不掉:可以弹窗提示 “空间不足,已生成空文件,请手动删除”。
类似这样
async function saveWithPicker() {
const savePicker = new picker.SaveFilePicker();
const result = await savePicker.save({
saveOptions: [{ fileName: 'test.txt', mimeType: 'text/plain' }]
});
const fileUri = result.uriList[0];
// 查所在目录空间
const dirUri = fileUri.substring(0, fileUri.lastIndexOf('/'));
const freeBytes = await statvfs.getFreeSize(dirUri);
const needSize = 1024 * 1024 * 10;
if (freeBytes < needSize) {
// 尝试删除占位文件
await fs.unlink(fileUri).catch((e: BusinessError) => {
// 删不掉:提示用户
AlertDialog.show({ message: '空间不足,导出失败。已生成空文件,请手动删除:' + fileUri });
});
return;
}
// 空间足够:继续写入...
}
mark
建议导出前先检测一下可用空间再导出吧
通过 应用及文件系统空间统计 去获取剩余空间存储
1、获取当前文件大小
2、获取剩余空间大小
3、在用户选择目录前比对
let context = this.getUIContext().getHostContext() as common.UIAbilityContext;
let path = context.filesDir;
statfs.getFreeSize(path, (err: BusinessError, number: number) => {
if (err) {
console.error(`Invoke getFreeSize failed, code is ${err.code}, message is ${err.message}`);
} else {
console.info(`Invoke getFreeSize succeeded, size is ${number}`);
}
});
在HarmonyOS NEXT中,导出文件前可使用FileAccessHelper的getFreeSize()接口检查目标路径剩余空间。若空间不足,主动中断写入流程并返回错误码(如ERROR_NO_SPACE)。也可在写入时监听IOException(如DiskFullException),捕获后立即结束导出操作。
当使用SaveButton(文件保存控件)导出时,用户选择路径后系统会预创建一个0字节占位文件并返回URI。此时可在onCreate回调中先获取该文件描述符,通过fs.fstatvfs(fd)获取目标存储卷剩余空间,与待导出文件大小比较。若空间不足,不执行写入操作并直接关闭描述符,同时可提示用户空间不足。此时占位文件无法删除(应用无外部存储删除权限),会残留0字节文件,但这是系统设计的正常行为,无法避免。提前检查空间只能在拿到URI后进行,因为选择路径前无法预知目标卷。

