Firebase Storage将文件下载到Android Q中的Pictures或Movie文件夹 [英] Firebase Storage download file to Pictures or Movie folder in Android Q
问题描述
我能够将文件从Firebase Storage下载到storage/emulated/0/Pictures
,这是大多数流行应用以及Facebook或Instagram使用的图片的默认文件夹.既然Android Q在存储和访问文件方面有很多行为上的变化,那么当我的应用在Android Q中运行时,我的应用将不再能够从存储桶中下载文件.
这是将文件从Firebase存储桶写入并下载到大容量存储默认文件夹(如图片,电影,文档等)的代码.它在Android M上有效,但在Q上无效.
String state = Environment.getExternalStorageState();
String type = "";
if (downloadUri.contains("jpg") || downloadUri.contains("jpeg")
|| downloadUri.contains("png") || downloadUri.contains("webp")
|| downloadUri.contains("tiff") || downloadUri.contains("tif")) {
type = ".jpg";
folderName = "Images";
}
if (downloadUri.contains(".gif")){
type = ".gif";
folderName = "Images";
}
if (downloadUri.contains(".mp4") || downloadUri.contains(".avi")){
type = ".mp4";
folderName = "Videos";
}
//Create a path from root folder of primary storage
File dir = new File(Environment.getExternalStorageDirectory().getAbsolutePath() + "/" + Environment.DIRECTORY_PICTURES + "/MY_APP_NAME");
if (Environment.MEDIA_MOUNTED.equals(state)){
try {
if (dir.mkdirs())
Log.d(TAG, "New folder is created.");
}
catch (Exception e) {
e.printStackTrace();
Crashlytics.logException(e);
}
}
//Create a new file
File filePath = new File(dir, UUID.randomUUID().toString() + type);
//Creating a reference to the link
StorageReference httpsReference = FirebaseStorage.getInstance().getReferenceFromUrl(download_link_of_file_from_Firebase_Storage_bucket);
//Getting the file from the server
httpsReference.getFile(filePath).addOnProgressListener(taskSnapshot ->
showProgressNotification(taskSnapshot.getBytesTransferred(), taskSnapshot.getTotalByteCount(), requestCode)
);
通过此操作,它将文件从服务器下载到路径为storage/emulated/0/Pictures/MY_APP_NAME
的设备存储中,但是在Android Q中,由于已弃用了许多API,如Environment.getExternalStorageDirectory()
,它将不再起作用.
使用android:requestLegacyExternalStorage=true
是一个临时解决方案,但很快将无法在Android 11及更高版本上使用.
所以我的问题是如何使用Firebase Storage API在默认的 Picture 或 Movie 文件夹中,而不是Android/data/com.myapp.package/files
文件夹中下载文件.
MediaStore
和ContentResolver
是否对此有解决方案?我需要应用哪些更改?
== NEW ANSWER ==
如果要监视下载进度,可以使用FirebaseStorage SDK的getStream()
,如下所示:
httpsReference.getStream((state, inputStream) -> {
long totalBytes = state.getTotalByteCount();
long bytesDownloaded = 0;
byte[] buffer = new byte[1024];
int size;
while ((size = inputStream.read(buffer)) != -1) {
stream.write(buffer, 0, size);
bytesDownloaded += size;
showProgressNotification(bytesDownloaded, totalBytes, requestCode);
}
// Close the stream at the end of the Task
inputStream.close();
stream.flush();
stream.close();
}).addOnSuccessListener(taskSnapshot -> {
showDownloadFinishedNotification(downloadedFileUri, downloadURL, true, requestCode);
//Mark task as complete so the progress download notification whether success of fail will become removable
taskCompleted();
contentValues.put(MediaStore.Files.FileColumns.IS_PENDING, false);
resolver.update(uriResolve, contentValues, null, null);
}).addOnFailureListener(e -> {
Log.w(TAG, "download:FAILURE", e);
try {
stream.flush();
stream.close();
} catch (IOException ioException) {
ioException.printStackTrace();
FirebaseCrashlytics.getInstance().recordException(ioException);
}
FirebaseCrashlytics.getInstance().recordException(e);
//Send failure
showDownloadFinishedNotification(null, downloadURL, false, requestCode);
//Mark task as complete
taskCompleted();
});
==旧答案==
最后,经过数小时的努力,我设法做到了,但是最后选择了使用.getBytes(maximum_file_size)
而不是.getFile(file_object)
.
非常感谢@Kasim提出了getBytes(maximum_file_size)
的思想,并提供了与InputStream
和OutputStream
一起工作的示例代码.通过搜索与I/O相关的SO主题也是一个很大的帮助此处和此处 >
这里的想法是 With this it will download the files from server to your device storage with path Using So my question is how can I download files using Firebase Storage APIs on default Picture or Movie folder that is in the root instead of Does ==NEW ANSWER== If you wanted to monitor the download progress you can use ==OLD ANSWER== Finally after tons of hours I manage to do it but using Big big thanks to @Kasim for bringing up the idea of The idea here is So here is the code that let you saved file from your Firebase Storage Bucket to your device default directories:
这篇关于Firebase Storage将文件下载到Android Q中的Pictures或Movie文件夹的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!.getByte(maximum_file_size)
将从存储桶中下载文件,并在其addOnSuccessListener
回调中返回byte[]
.缺点是您必须指定允许下载的文件大小,并且无法完成AFAIK的下载进度计算,除非您对outputStream.write(0,0,0);
做过一些工作,但我试图像storage/emulated/0/Pictures/MY_APP_NAME
but with Android Q this will no longer work as many APIs became deprecated like Environment.getExternalStorageDirectory()
.android:requestLegacyExternalStorage=true
is a temporary solution but will no longer work soon on Android 11 and above.Android/data/com.myapp.package/files
.MediaStore
and ContentResolver
has solution for this? What changes do I need to apply?getStream()
of FirebaseStorage SDK like this:httpsReference.getStream((state, inputStream) -> {
long totalBytes = state.getTotalByteCount();
long bytesDownloaded = 0;
byte[] buffer = new byte[1024];
int size;
while ((size = inputStream.read(buffer)) != -1) {
stream.write(buffer, 0, size);
bytesDownloaded += size;
showProgressNotification(bytesDownloaded, totalBytes, requestCode);
}
// Close the stream at the end of the Task
inputStream.close();
stream.flush();
stream.close();
}).addOnSuccessListener(taskSnapshot -> {
showDownloadFinishedNotification(downloadedFileUri, downloadURL, true, requestCode);
//Mark task as complete so the progress download notification whether success of fail will become removable
taskCompleted();
contentValues.put(MediaStore.Files.FileColumns.IS_PENDING, false);
resolver.update(uriResolve, contentValues, null, null);
}).addOnFailureListener(e -> {
Log.w(TAG, "download:FAILURE", e);
try {
stream.flush();
stream.close();
} catch (IOException ioException) {
ioException.printStackTrace();
FirebaseCrashlytics.getInstance().recordException(ioException);
}
FirebaseCrashlytics.getInstance().recordException(e);
//Send failure
showDownloadFinishedNotification(null, downloadURL, false, requestCode);
//Mark task as complete
taskCompleted();
});
.getBytes(maximum_file_size)
instead of .getFile(file_object)
as last resort.getBytes(maximum_file_size)
with also sample code working with InputStream
and OutputStream
.By searching across S.O topic related to I/O also is a big help here and here.getByte(maximum_file_size)
will download the file from the bucket and return a byte[]
on its addOnSuccessListener
callback. The downside is you must specify the file size you allowed to download and no download progress computation can be done AFAIK unless you do some work with outputStream.write(0,0,0);
I tried to write it chunk by chunk like here but the solution is throwing ArrayIndexOutOfBoundsException
since you must be accurate on working with index into an array.storage/emulated/0/Pictures, storage/emulated/0/Movies, storage/emulated/0/Documents, you name it
//Member variable but depending on your scope
private ByteArrayInputStream inputStream;
private Uri downloadedFileUri;
private OutputStream stream;
//Creating a reference to the link
StorageReference httpsReference = FirebaseStorage.getInstance().getReferenceFromUrl(downloadURL);
Uri contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
String type = "";
String mime = "";
String folderName = "";
if (downloadURL.contains("jpg") || downloadURL.contains("jpeg")
|| downloadURL.contains("png") || downloadURL.contains("webp")
|| downloadURL.contains("tiff") || downloadURL.contains("tif")) {
type = ".jpg";
mime = "image/*";
contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
folderName = Environment.DIRECTORY_PICTURES;
}
if (downloadURL.contains(".gif")){
type = ".gif";
mime = "image/*";
contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
folderName = Environment.DIRECTORY_PICTURES;
}
if (downloadURL.contains(".mp4") || downloadURL.contains(".avi")){
type = ".mp4";
mime = "video/*";
contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
folderName = Environment.DIRECTORY_MOVIES;
}
if (downloadURL.contains(".mp3")){
type = ".mp3";
mime = "audio/*";
contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
folderName = Environment.DIRECTORY_MUSIC;
}
final String relativeLocation = folderName + "/" + getString(R.string.app_name);
final ContentValues contentValues = new ContentValues();
contentValues.put(MediaStore.MediaColumns.DISPLAY_NAME, UUID.randomUUID().toString() + type);
contentValues.put(MediaStore.MediaColumns.MIME_TYPE, mime); //Cannot be */*
contentValues.put(MediaStore.MediaColumns.RELATIVE_PATH, relativeLocation);
ContentResolver resolver = getContentResolver();
Uri uriResolve = resolver.insert(contentUri, contentValues);
try {
if (uriResolve == null || uriResolve.getPath() == null) {
throw new IOException("Failed to create new MediaStore record.");
}
stream = resolver.openOutputStream(uriResolve);
//This is 1GB change this depending on you requirements
httpsReference.getBytes(1024 * 1024 * 1024)
.addOnSuccessListener(bytes -> {
try {
int bytesRead;
inputStream = new ByteArrayInputStream(bytes);
while ((bytesRead = inputStream.read(bytes)) > 0) {
stream.write(bytes, 0, bytesRead);
}
inputStream.close();
stream.flush();
stream.close();
//FINISH
} catch (IOException e) {
closeSession(resolver, uriResolve, e);
e.printStackTrace();
Crashlytics.logException(e);
}
});
} catch (IOException e) {
closeSession(resolver, uriResolve, e);
e.printStackTrace();
Crashlytics.logException(e);
}