提问人:0verlord 提问时间:10/26/2023 最后编辑:0verlord 更新时间:10/26/2023 访问量:107
在 Android 11+ 中下载/保存文件时出现问题
Problem with downloading/saving files in Android 11+
问:
- 二手设备的Android版本 - 13
- 路径 1
给出文本错误 - 此路径/storage/emulated/0/Android/media/my_app/files/img.jpg
- 路径 2
保存,但位于用户无法访问的数据文件夹中(仅在 Android Studio 上)。/storage/emulated/0/Android/data/my_app/files/img.jpg
我希望你们一切都好。我目前正面临着使用我的 Android 应用程序的挑战,我真的可以使用社区的一些指导。
问题来了:我正在开发一个 Android 应用程序,我需要将文件下载/保存到设备的存储中,以便用户可以访问它们。 我正在使用 Capacitor 进行开发。但是,我在尝试将这些文件保存到正确的位置时遇到了问题。
我已经试过了
- Mediastore.saveToDownloads from '@agorapulse/capacitor-mediastore' - 我的路径错误 -
/storage/emulated/0/Android/media/my_app/files/img.jpg
- Filesystem.writeFile 从 '@capacitor/filesystem' 返回
{"uri":"file:///storage/emulated/0/Android/data/my_app/files/img.jpg"}
在这里,我可以看到我的文件,但只能在Android Studio中。
我正在尝试让用户保存文件,因为它可以在 iOS 中完成(使用 ),这样他就可以通过本机文件应用程序直接访问它Directory.ExternalStorage
我已经在我的存储中声明了读取和写入外部存储的必要权限。但是,尽管如此,我仍然收到错误或文件似乎没有按预期保存。AndroidManifest.xml
- 二手设备的Android版本 - 13
- “@capacitor/核心”: “^5.0.0”
- “@capacitor/文件系统”: “^5.1.2”
- “@capacitor/设备”: “^5.0.0”
<!-- Permissions -->
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.READ_MEDIA_AUDIO"/>
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
<uses-permission android:name="android.permission.READ_MEDIA_VIDEO"/>
<uses-permission android:name="android.permission.RECORD_VIDEO" />
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES"/>
<uses-permission android:name="android.permission.READ_CONTACTS" />
<uses-permission android:name="android.permission.WRITE_CONTACTS" />
// Test of permission for managing external storage
<!-- <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" /> -->
// For Android 10 and older
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
import { Device } from '@capacitor/device';
import type { PermissionStatus as FilePermissionStatus } from '@capacitor/filesystem';
import { Directory, Filesystem } from '@capacitor/filesystem';
const downloadFile = async (file: FileModel): Promise<FileStatusEnum> => {
if (isNativeMobile) {
const media = await $api.file.media(file.apiUrl);
if (isBlob(media)) {
const savedFile = await saveFile(
media as Blob,
file.key,
file.name,
file.type,
file.mimeType
);
if (savedFile !== undefined) {
return FileStatusEnum.Success;
} else {
return FileStatusEnum.Error;
}
} else {
return FileStatusEnum.Error;
}
} else {
const media = await $api.file.media(file.apiUrl);
if (isBlob(media)) {
const link = Object.assign(document.createElement('a'), {
href: URL.createObjectURL(media as Blob),
download: `${file.name}.${file.type}`,
});
link.click();
}
}
return FileStatusEnum.Success;
};
const saveFile = async (
file: Blob,
internal: string,
name: string,
ext: string,
mimeType: string
): Promise<string | undefined> => {
if (hasFilePermission.value === false) {
let status: FilePermissionStatus = await Filesystem.checkPermissions();
if (status.publicStorage !== 'granted') {
status = await Filesystem.requestPermissions();
}
// если пользователь отказался давать разрешение
if (status.publicStorage !== 'granted') {
return undefined;
}
hasFilePermission.value = true;
}
const base64Data = await blobToString(file);
if (name.length > 120) {
name = name.slice(0, 120);
}
const deviceInfo = await Device.getInfo();
const androidCase = deviceInfo.platform === 'android';
// Saving the file using the Filesystem plugin
// Note that for Android, we use the Cache directory as ExternalStorage isn't writable
// On iOS, we use the ExternalStorage directory - Documents directory
const savedFile = await Filesystem.writeFile({
path: name + '.' + ext,
data: base64Data,
directory: androidCase ? Directory.External : Directory.ExternalStorage,
});
const downloadedFile = await Mediastore.saveToDownloads({
filename: name + '.' + ext,
path:
'/storage/emulated/0/Android/media/my_app/files/' +
name +
'.' +
ext,
});
// TODO - Remove this console.log
console.log('---Saved File---', JSON.stringify(savedFile));
console.log('---Downloaded File---', JSON.stringify(downloadedFile));
const fileModel: FilePickModel = {
internal: internal,
name: name,
path: savedFile.uri,
mimeType: mimeType,
size: file.size,
payload: '',
status: FileStatusEnum.Success,
media: undefined,
};
files.value.push(fileModel);
const fileStore = useFileStore();
fileStore.add({
mimeType: mimeType,
path: savedFile.uri,
name: name,
size: file.size,
} as FileCacheModel);
return savedFile.uri;
};
如果有人遇到类似的问题或对如何正确将文件保存到 Android 存储有任何见解,我将非常感谢您的帮助。您的专业知识将有很大帮助!
Mediastore.saveTo下载
public void saveToDownloads(PluginCall call) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
call.reject("method requires API 29+");
return;
}
String fileName = call.getString("filename");
String path = call.getString("path");
if (path == null) {
call.reject("path is required");
return;
} else if (path.startsWith("file:///")) {
path = path.substring(8);
}
String uri;
try {
uri = this.implementation.saveToDownloads(this.getActivity().getApplicationContext(), fileName, path);
} catch (Exception e) {
call.reject(e.getMessage());
return;
}
JSObject ret = new JSObject();
ret.put("uri", uri);
call.resolve(ret);
}
Filesystem.write文件
public void writeFile(PluginCall call) {
String path = call.getString("path");
String data = call.getString("data");
Boolean recursive = call.getBoolean("recursive", false);
if (path == null) {
Logger.error(getLogTag(), "No path or filename retrieved from call", null);
call.reject("NO_PATH");
return;
}
if (data == null) {
Logger.error(getLogTag(), "No data retrieved from call", null);
call.reject("NO_DATA");
return;
}
String directory = getDirectoryParameter(call);
if (directory != null) {
if (isPublicDirectory(directory) && !isStoragePermissionGranted()) {
requestAllPermissions(call, "permissionCallback");
} else {
// create directory because it might not exist
File androidDir = implementation.getDirectory(directory);
if (androidDir != null) {
if (androidDir.exists() || androidDir.mkdirs()) {
// path might include directories as well
File fileObject = new File(androidDir, path);
if (fileObject.getParentFile().exists() || (recursive && fileObject.getParentFile().mkdirs())) {
saveFile(call, fileObject, data);
} else {
call.reject("Parent folder doesn't exist");
}
} else {
Logger.error(getLogTag(), "Not able to create '" + directory + "'!", null);
call.reject("NOT_CREATED_DIR");
}
} else {
Logger.error(getLogTag(), "Directory ID '" + directory + "' is not supported by plugin", null);
call.reject("INVALID_DIR");
}
}
} else {
// check file:// or no scheme uris
Uri u = Uri.parse(path);
if (u.getScheme() == null || u.getScheme().equals("file")) {
File fileObject = new File(u.getPath());
// do not know where the file is being store so checking the permission to be secure
// TODO to prevent permission checking we need a property from the call
if (!isStoragePermissionGranted()) {
requestAllPermissions(call, "permissionCallback");
} else {
if (
fileObject.getParentFile() == null ||
fileObject.getParentFile().exists() ||
(recursive && fileObject.getParentFile().mkdirs())
) {
saveFile(call, fileObject, data);
} else {
call.reject("Parent folder doesn't exist");
}
}
} else {
call.reject(u.getScheme() + " scheme not supported");
}
}
}
private void saveFile(PluginCall call, File file, String data) {
String encoding = call.getString("encoding");
boolean append = call.getBoolean("append", false);
Charset charset = implementation.getEncoding(encoding);
if (encoding != null && charset == null) {
call.reject("Unsupported encoding provided: " + encoding);
return;
}
try {
implementation.saveFile(file, data, charset, append);
// update mediaStore index only if file was written to external storage
if (isPublicDirectory(getDirectoryParameter(call))) {
MediaScannerConnection.scanFile(getContext(), new String[] { file.getAbsolutePath() }, null, null);
}
Logger.debug(getLogTag(), "File '" + file.getAbsolutePath() + "' saved!");
JSObject result = new JSObject();
result.put("uri", Uri.fromFile(file).toString());
call.resolve(result);
} catch (IOException ex) {
Logger.error(
getLogTag(),
"Creating file '" + file.getPath() + "' with charset '" + charset + "' failed. Error: " + ex.getMessage(),
ex
);
call.reject("FILE_NOTCREATED");
} catch (IllegalArgumentException ex) {
call.reject("The supplied data is not valid base64 content.");
}
}
答: 暂无答案
评论
${name}.${ext}
/Downloads/${name}.${ext}