在2016年Google I / O会议期间,Firebase被重新引入到开发人员社区中,这是为网页和移动应用程序提供快速后端支持的主要工具。 本教程将向您介绍可用于Android应用程序的文件存储和检索功能。

要了解有关Firebase的实时数据库,分析,崩溃报告和身份验证的更多信息,请在Envato Tuts +上查看我们的其他一些教程

身份验证设置

在开始使用Firebase Storage之前,您需要确保您的用户已通过身份验证,或在Firebase控制台中更改身份验证要求规则,以允许未经身份验证的用户访问和上传文件。 为了使事情简单,我们将进行后者。 首先,通过在左侧导航栏中选择“ 存储”进入Firebase的“存储”部分。



firebase 架构 firebase storage_java


接下来,您会注意到“ 存储”屏幕顶部有两个选项卡: 文件规则



firebase 架构 firebase storage_jvm_02


选择“ 规则”选项卡,并在该行上allow read, write: if request.auth != null; ,将!=更改为==并单击PUBLISH按钮。



firebase 架构 firebase storage_java_03


现在,您应用程序的任何用户都应该能够从Firebase后端上传或下载文件。 尽管这对于生产环境而言不是理想的选择,但它使学习Firebase Storage变得容易得多,而无需深入研究身份验证代码。

手动上传文件

能够从应用程序上传文件虽然很棒,但有时您只是想将文件存储在易于访问并下拉到应用程序中的某个位置。 这是能够从Firebase控制台手动上传文件的地方。 在“ 文件”标签下,您会看到一个名为上传文件的蓝色按钮。



firebase 架构 firebase storage_firebase 架构_04


单击该按钮,然后选择要上传的文件,它将显示在Firebase存储中。



firebase 架构 firebase storage_android_05


在控制台中选择该文件将显示一个详细信息视图,使您可以检查以前已上传的文件。



firebase 架构 firebase storage_android_06


从Android应用程序下载文件

现在您已经在Firebase中存储了一个文件,让我们继续将其下拉到应用程序中。 我们将在MainActivity中使用一个简单的布局,该布局包含一个id为image的ImageView

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="https://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <ImageView
        android:id="@+id/image"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />
</RelativeLayout>

为了访问Firebase Storage文件,您需要首先获取对FirebaseStorage对象的引用,然后创建对项目URL和要下载的文件的StorageReference 。 您可以在Firebase控制台的“ 存储”的“ 文件”部分顶部找到项目的URL。

FirebaseStorage storage = FirebaseStorage.getInstance();
StorageReference storageRef = storage.getReferenceFromUrl("gs://tutsplus-firebase.appspot.com").child("android.jpg");

接下来,您可以创建一个File对象,并通过将新的File对象作为参数传递给StorageReference上的getFile来尝试加载所需的文件。 由于此操作是异步发生的,因此可以将OnSuccessListenerOnFailureListener添加到您的调用中,以处理这两种情况。

try {
    final File localFile = File.createTempFile("images", "jpg");
    storageRef.getFile(localFile).addOnSuccessListener(new OnSuccessListener<FileDownloadTask.TaskSnapshot>() {
        @Override
        public void onSuccess(FileDownloadTask.TaskSnapshot taskSnapshot) {
            Bitmap bitmap = BitmapFactory.decodeFile(localFile.getAbsolutePath());
            mImageView.setImageBitmap(bitmap);

        }
    }).addOnFailureListener(new OnFailureListener() {
        @Override
        public void onFailure(@NonNull Exception exception) {
        }
    });
} catch (IOException e ) {}

OnSuccessListener onSuccess() ,可以获取FileDownloadTask.TaskSnapshot对象并检索文件,在该文件中,我们将图像设置为ImageView



firebase 架构 firebase storage_jvm_07


将文件下载为字节数组

如果只需要将文件下载为byte[]而不需要作为文件下载(在将图像加载到ImageView时更可能发生这种情况),则可以类似的方式检索字节。

final long ONE_MEGABYTE = 1024 * 1024;
storageRef.getBytes(ONE_MEGABYTE).addOnSuccessListener(new OnSuccessListener<byte[]>() {
    @Override
    public void onSuccess(byte[] bytes) {
        Bitmap bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
        mImageView.setImageBitmap(bitmap);
    }
});

获取文件的URL

在某些情况下,您不需要存储文件的实际数据,而需要URL。 您可以通过使用StorageReference上的getDownloadUrl()方法以与后两个示例类似的方式执行此操作,这将为您提供一个指向文件位置的Uri

storageRef.getDownloadUrl().addOnSuccessListener(new OnSuccessListener<Uri>() {
    @Override
    public void onSuccess(Uri uri) {
        Log.e("Tuts+", "uri: " + uri.toString());
        //Handle whatever you're going to do with the URL here
    }
});

上述方法将打印出我们之前在Android Monitor中手动上传的图像的URL。

E/Tuts+: uri: https://firebasestorage.googleapis.com/v0/b/tutsplus-firebase.appspot.com/o/android.jpg?alt=media&token=1828111c-78e7-4640-b418-c65e7571576a

从Android应用上传

现在您知道了如何从Firebase下载文件,是时候进行上传了。 从Firebase Storage下载时,您看到的每种数据形式的过程都非常相似。 上载没有什么不同,因此我们将简单地介绍如何将文件从您的应用程序移至Firebase Storage。

上载字节数组

与下载一样,您将需要获取对FirebaseStorage对象的引用,并创建对新文件存储位置的引用作为StorageReference 。 对于此示例,我们将在ImageView显示ic_launcher.png ,然后将其上传为字节数组。

FirebaseStorage storage = FirebaseStorage.getInstance();
StorageReference storageReference = storage.getReferenceFromUrl("gs://tutsplus-firebase.appspot.com").child("ic_launcher.png");

接下来,我们需要通过ImageView从存储在内存中的图像获取字节数组。 这是通过将其检索为Bitmap ,将其压缩为ByteArrayOutputStream ,然后将其转换为byte[]

mImageView.setDrawingCacheEnabled(true);
mImageView.measure(View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED), View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED));
mImageView.layout(0, 0, mImageView.getMeasuredWidth(), mImageView.getMeasuredHeight());
mImageView.buildDrawingCache();
Bitmap bitmap = Bitmap.createBitmap(mImageView.getDrawingCache());

ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, outputStream);
byte[] data = outputStream.toByteArray();

最后,您可以通过调用putBytes(byte[])将图像上传到Firebase来创建UploadTask 。 此UploadTask也可以具有OnFailureListener关联的OnSuccessListenerOnFailureListener

UploadTask uploadTask = storageReference.putBytes(data);
uploadTask.addOnFailureListener(new OnFailureListener() {
    @Override
    public void onFailure(@NonNull Exception exception) {
    }
}).addOnSuccessListener(new OnSuccessListener<UploadTask.TaskSnapshot>() {
    @Override
    public void onSuccess(UploadTask.TaskSnapshot taskSnapshot) {
    }
});

当您检查Firebase控制台的存储页面时,您应该在文件列表中看到ic_launcher.png



firebase 架构 firebase storage_jvm_08


InputStream或文件上传

既然您知道如何上传字节数组,那么其他两种上传类型应该非常直观。 假设我们在原始资源文件夹中有一个名为test.txt的文本文件。 我们可以将其读入InputStream ,然后使用StorageReferenceputStream(InputStream)方法上传。

FirebaseStorage storage = FirebaseStorage.getInstance();
StorageReference storageReference = storage.getReferenceFromUrl("gs://tutsplus-firebase.appspot.com").child("test.txt");

InputStream stream = getResources().openRawResource(R.raw.test);

UploadTask uploadTask = storageReference.putStream(stream);



firebase 架构 firebase storage_android_09


上载现有文件同样容易:只需获取对该文件的引用,然后使用指向您文件的URI调用putFile(Uri) 。 对于此示例,我们将在代码中创建一个空的临时文件。

FirebaseStorage storage = FirebaseStorage.getInstance();
StorageReference storageReference = storage.getReferenceFromUrl("gs://tutsplus-firebase.appspot.com").child("test2.txt");

File file = null;
try {
    file = File.createTempFile("test2", "txt");
} catch( IOException e ) {

}
UploadTask uploadTask = storageReference.putFile(Uri.fromFile(file));



firebase 架构 firebase storage_jvm_10


控制上传和使用回调

虽然到目前为止我们上传的文件很小,但有时您上传的文件可能会花费很多时间。 Firebase使用UploadTask提供了一些方法,这些方法可让您控制上传的流程以及侦听进度和状态更改。 这些方法包括pause()resume()cancel()pause()resume()将允许您暂停和恢复UploadTask ,而cancel()将完全停止它。 此外,您可以使用OnPauseListenerOnProgressListener跟踪上载进度和暂停状态。

mPause = (Button) findViewById(R.id.pause);
mResume = (Button) findViewById(R.id.resume);

mPause.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View view) {
        mUploadTask.pause();
    }
});

mResume.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View view) {
        mUploadTask.resume();
    }
});


FirebaseStorage storage = FirebaseStorage.getInstance();
StorageReference storageReference = storage.getReferenceFromUrl("gs://tutsplus-firebase.appspot.com").child("image.jpg");

InputStream stream = getResources().openRawResource(R.raw.image);

mUploadTask = storageReference.putStream(stream);

mUploadTask.addOnPausedListener(new OnPausedListener<UploadTask.TaskSnapshot>() {
    @Override
    public void onPaused(UploadTask.TaskSnapshot taskSnapshot) {
        Log.e("Tuts+", "upload paused!");
    }
});

mUploadTask.addOnProgressListener(new OnProgressListener<UploadTask.TaskSnapshot>() {
    @Override
    public void onProgress(UploadTask.TaskSnapshot taskSnapshot) {
        Log.e("Tuts+", "Bytes uploaded: " + taskSnapshot.getBytesTransferred());
    }
});

上面的代码将允许您控制较大的图像(在这种情况下为1MB)的上传过程,并在按下暂停和恢复按钮时在Android日志中查看其状态更改。

E/Tuts+: Bytes uploaded: 524288
E/Tuts+: upload paused!
E/Tuts+: Bytes uploaded: 786432

处理Android活动生命周期

正如任何Android开发人员所能证明的那样,有时Android活动生命周期可能会导致意外问题。 问题的常见来源之一是侦听器的持续时间长于其父“ Activity ,这可能是附加到Firebase存储任务的成功/失败侦听器的情况。

如果在执行任务时销毁并重新创建了Activity (例如在屏幕上旋转),则在任务完成时,您可能会遇到NullPointerException 。 为避免这种情况,您将希望将StorageReference保存为String ,并处于onSaveInstanceState(Bundle)方法的out状态Bundle中,然后对其进行检索,并将成功的侦听器添加到与该StorageReference关联的每个FileDownloadTaskFileUploadTask

@Override
protected void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);

    if (mStorageReference != null) {
        outState.putString(EXTRA_STORAGE_REFERENCE_KEY, mStorageReference.toString());
    }
}

@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
    super.onRestoreInstanceState(savedInstanceState);

    final String stringRef = savedInstanceState.getString(EXTRA_STORAGE_REFERENCE_KEY);

    if (stringRef == null) {
        return;
    }

    mStorageReference = FirebaseStorage.getInstance().getReferenceFromUrl(stringRef);
    List<FileDownloadTask> tasks = mStorageReference.getActiveDownloadTasks();
    for( FileDownloadTask task : tasks ) {
        task.addOnSuccessListener(this, new OnSuccessListener<FileDownloadTask.TaskSnapshot>() {
            @Override
            public void onSuccess(FileDownloadTask.TaskSnapshot taskSnapshot) {
                Log.e("Tuts+", "download successful!");
            }
        });
    }
}

结论

在本教程中,您学到了很多有关Firebase及其文件存储可用选项的知识。 现在,您应该能够上载和下载文件,控制数据传输,并在发生事务时处理应用程序活动生命周期中的更改。

尽管我们只是初步了解了Firebase的功能,但了解此工具有望使您能够扩展自己的应用程序的功能并为用户提供出色的体验。

翻译自: https://code.tutsplus.com/tutorials/firebase-for-android-file-storage--cms-27376