Firebase Storage上传/下载速度非常慢

11

故事

我在我的应用程序中使用 Firebase 存储来上传大文件到 Firebase 存储。这些文件大多是视频,有时甚至可以超过 2GB。

我的做法

以下是我所做的事情。

UploadTask originalUpload = originalDestination.putFile(Uri.fromFile(originalSource));
            mCurrentUploadTask = originalUpload;
            originalUpload.addOnProgressListener(mOnProgressUpdateListener);
originalUpload.addOnSuccessListener(mOriginalSuccessListener);

仅作通知,我也正在将这些同步任务转换为异步任务,因为我需要在后台处理所有内容使用,

Tasks.await(originalUpload);

问题
问题很奇怪和意外,上传/下载功能完美,但非常缓慢。
我在1 MBps的良好互联网连接上,但是这些文件从未以此速度传输。它大约是100-150 KBps,几乎是我的网络可用速度的15%。
在提出这一说法之前,我已经在几个不同的网络上进行了多次测试。无论何时,我都未发现Firebase充分利用可用带宽,而其他非Firebase的下载/上传速度都能达到最大速度。
我的应用程序需要下载和上传大文件,因此不能承受这样的低速传输。这完全出乎Firebase的意料。
请告诉我是否在我的实施方面有任何错误或者这是Firebase固有的问题?
更新
我也遇到了仅限上传的问题。每当我正在下载某些东西并且只是关闭Wifi时,下载就会被取消并出现此错误。
com.google.firebase.storage.StorageException: An unknown error occurred, please check the HTTP result code and inner exception for server response.

Read error: ssl=0xb7e7a510: I/O error during system call, Connection timed out
                                                                           javax.net.ssl.SSLException: Read error: ssl=0xb7e7a510: I/O error during system call, Connection timed out
                                                                               at com.android.org.conscrypt.NativeCrypto.SSL_read(Native Method)
                                                                               at com.android.org.conscrypt.OpenSSLSocketImpl$SSLInputStream.read(OpenSSLSocketImpl.java:699)
                                                                               at com.android.okio.Okio$2.read(Okio.java:113)
                                                                               at com.android.okio.RealBufferedSource.read(RealBufferedSource.java:48)
                                                                               at com.android.okhttp.internal.http.HttpConnection$FixedLengthSource.read(HttpConnection.java:446)
                                                                               at com.android.okio.RealBufferedSource$1.read(RealBufferedSource.java:168)
                                                                               at java.io.InputStream.read(InputStream.java:162)
                                                                               at com.google.firebase.storage.FileDownloadTask.run(Unknown Source)
                                                                               at com.google.firebase.storage.StorageTask$5.run(Unknown Source)
                                                                               at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1112)
                                                                               at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:587)
                                                                               at java.lang.Thread.run(Thread.java:818)

但是当我使用相同的方法上传文件时,它只是等待连接,如果我重新打开Wifi,它会重新连接并恢复。为什么问题只出现在下载文件时?是getFile()API再次出问题了吗?

值得注意的是,我没有更改上传和下载的超时设置。它们都保持默认设置。


你尝试过直接使用DownloadUrl进行下载,而不是使用Stream/FileDownloadTask吗?这样会更好吗? - Benjamin Wulfe
您IP地址为143.198.54.68,由于运营成本限制,当前对于免费用户的使用频率限制为每个IP每72小时10次对话,如需解除限制,请点击左下角设置图标按钮(手机用户先点击左上角菜单按钮)。 - Reaz Murshed
@ReazMurshed 我正在使用 Firebase 提供的几乎所有服务,这就是为什么我选择使用 Firebase 存储的原因。它还由 Google Cloud 存储支持,因此我可以享受到它所提供的所有好处。那么为什么我不应该将大文件存储在 Firebase 存储中呢?有哪些缺点呢? - Aritra Roy
@ReazMurshed 实际上,我没有维护自己的服务器。如果我这样做了,那么FTP将是我上传/下载的选择。您是否认为使用Firebase进行大文件存储,尤其是对于庞大的用户群体,存在任何缺点? - Aritra Roy
是的,当用户基数庞大时,您可能还需要考虑服务器响应时间。因此,我不建议上传大文件。这样,您也可以在其他地方优化与Firebase的连接,以提供更流畅的用户体验。 - Reaz Murshed
显示剩余4条评论
2个回答

3

我提供以下内容并非是要反驳你的观察结果,而只是作为一个比较点。作为测试,我使用了下面的代码来上传一个大小约为50MB的文件,并计时进度。该测试在三星Galaxy S3(4.4.2)上运行,使用家庭WiFi连接(在加利福尼亚州)。平均吞吐量大约为760KB/秒。

我在其他手机设备上运行了相同的测试,使用相同的网络,吞吐量范围从300KB/秒到850KB/秒不等(Moto X Pure; Marshmallow)。

private void uploadTest() {
    // This file is ~50MB
    final File file = new File("/storage/emulated/0/DCIM/Camera/20160821_101145.mp4");
    // Upload the file
    Log.i(TAG, String.format("uploadTest: Starting upload of %5.2fMB file",
            file.length()/1024.0/1024.0));

    FirebaseStorage.getInstance().getReference("test").putFile(Uri.fromFile(file))
            .addOnSuccessListener(new OnSuccessListener<UploadTask.TaskSnapshot>() {
                @Override
                public void onSuccess(UploadTask.TaskSnapshot taskSnapshot) {
                    Log.i(TAG, "onSuccess: Done");
                }
            }).addOnProgressListener(new OnProgressListener<UploadTask.TaskSnapshot>() {
        @Override
        public void onProgress(UploadTask.TaskSnapshot taskSnapshot) {
            Log.i(TAG, String.format("onProgress: %5.2f MB transferred",
                    taskSnapshot.getBytesTransferred()/1024.0/1024.0));
        }
    });
}

这是生成的logcat,为了使列表更紧凑,删除了一些进度行:
08-21 11:39:17.539 18427-18427/com.qbix.test I/MainActivity: uploadTest: Starting upload of 51.79MB file
08-21 11:39:17.559 18427-18427/com.qbix.test I/MainActivity: onProgress:  0.00 MB transferred
08-21 11:39:25.117 18427-18427/com.qbix.test I/MainActivity: onProgress:  5.00 MB transferred
08-21 11:39:31.654 18427-18427/com.qbix.test I/MainActivity: onProgress: 10.00 MB transferred
08-21 11:39:38.711 18427-18427/com.qbix.test I/MainActivity: onProgress: 15.00 MB transferred
08-21 11:39:45.088 18427-18427/com.qbix.test I/MainActivity: onProgress: 20.00 MB transferred
08-21 11:39:51.375 18427-18427/com.qbix.test I/MainActivity: onProgress: 25.00 MB transferred
08-21 11:39:57.411 18427-18427/com.qbix.test I/MainActivity: onProgress: 30.00 MB transferred
08-21 11:40:03.408 18427-18427/com.qbix.test I/MainActivity: onProgress: 35.00 MB transferred
08-21 11:40:10.886 18427-18427/com.qbix.test I/MainActivity: onProgress: 40.00 MB transferred
08-21 11:40:17.233 18427-18427/com.qbix.test I/MainActivity: onProgress: 45.00 MB transferred
08-21 11:40:23.069 18427-18427/com.qbix.test I/MainActivity: onProgress: 50.00 MB transferred
08-21 11:40:25.792 18427-18427/com.qbix.test I/MainActivity: onProgress: 51.79 MB transferred
08-21 11:40:25.792 18427-18427/com.qbix.test I/MainActivity: onSuccess: Done 

谢谢测试。顺便问一下,你用来测试的网络总带宽是多少?我从未得到超过总带宽15%的速度。 - Aritra Roy
我明白了。很难找出我到底做错了什么。这可能是因为我在后台线程中使用它吗? - Aritra Roy
Aritra - 你能否在使用getDownloadUrl()返回的URL下载时测试下载速度?这很重要,因为这将给我们(Google)一个指示,告诉我们问题是否出现在我们的客户端SDK中,还是在网络路由中。此外,请检查您的设备上是否有其他正在运行的进程可能正在占用CPU周期。 - Benjamin Wulfe
@qbix 我也在不同的设备上进行了测试,从 Moto E 这样的低端设备到 OnePlus 3 这样的高端设备,但速度在所有设备上都持续很差。 - Aritra Roy
@BenjaminWulfe 我已经使用下载链接直接下载了文件,这次下载速度达到了最大值。但是如果我尝试使用getFile() API,每次都非常慢。因此,我认为问题出在getFile() API上。我还检查了其他正在运行的进程,一切都很干净。我还在几台设备上测试了它,在几个Android版本上进行了测试,以公正起见。 - Aritra Roy
显示剩余2条评论

2

我已经进行了一些测试,试图找出为什么您看到了糟糕的性能。

当使用getStream每次读取单个字节时,我能够复制出现糟糕表现的情况,但是通过读取一个合理大小的字节缓冲区(256kb缓冲区)可以解决这个问题。

在修复这个问题之后,我看到的性能与直接使用httpsUrlConnection下载相同。

我脑海中浮现出三种可能:

1)在DNS解析期间(对于我们的多个IP),您会看到超时,并且连接池在上传期间不起作用。但是,这只能解释为什么上传(分块)会变慢。下载没有分块。

2)您可能位于我们尚未针对其进行优化的地区。我们很快将在亚洲和欧洲推出本地支持以优化性能。

3)也许您正在与httpsurlconnection之外的其他东西进行比较?您能否提供有关您的测试的更多详细信息?

以下是我的测试:

        final long startTime = System.currentTimeMillis();
        // Test via Firebase
        mStorage.child(downloadPath).getStream(new StreamDownloadTask.StreamProcessor() {
               @Override
               public void doInBackground(StreamDownloadTask.TaskSnapshot taskSnapshot,
                      final InputStream inputStream) throws IOException {
                 readStream("FB", inputStream, startTime);
               }
        });

        //Test with AsyncTask
        AsyncTask<Void, Void, Void> task = new AsyncTask<Void, Void, Void>() {
            @Override
            protected Void doInBackground(Void... voids) {
                URL url = null;
                try {
                    url = new URL(uri.toString());
                } catch (MalformedURLException e) {
                    e.printStackTrace();
                }
                try {
                    HttpsURLConnection connection = (HttpsURLConnection) url.openConnection();
                    connection.connect();
                    if (connection.getResponseCode() != HttpURLConnection.HTTP_OK) {
                        return null;
                    }
                    InputStream inputStream = connection.getInputStream();
                    readStream("ASYNC_TASK", inputStream, startTime);
                    inputStream.close();

                }
                catch(IOException e) {

                }

                return null;
            }
        };

        task.execute();

    private void readStream(String desc, InputStream inputStream, long startTime) {
      long bytesRead = 0;
      byte[] buffer = new byte[256*1024];
      int b = 1;
      try {
        while (b > 0) {
            b = inputStream.read(buffer);
            bytesRead += b;
                Log.i("giug", "PROGRESS_" + desc + ": bytesperMS:" +
                        (bytesRead / (System.currentTimeMillis() - startTime)));
        }
        inputStream.close();
      } catch (IOException e) {
          e.printStackTrace();
      }
   } 

但是我一直在使用getFile()而不是getStream()。你能分享你的代码,这样我就可以实现它并回复你结果吗?我遇到了一个非常奇怪的问题,并更新了我的问题。你能帮忙解决一下吗? - Aritra Roy
您IP地址为143.198.54.68,由于运营成本限制,当前对于免费用户的使用频率限制为每个IP每72小时10次对话,如需解除限制,请点击左下角设置图标按钮(手机用户先点击左上角菜单按钮)。 - Benjamin Wulfe
不,传输速度慢的问题与两者都有关。我已经在这个问题上单独提出了一个问题:http://stackoverflow.com/questions/38993429/firebase-storage-downloads-doesnt-resume-on-network-reconnection。如果可能的话,请帮助解决这个问题。Firebase存储似乎存在几个问题,如果出现这种错误,我将不得不停止在我的应用程序中使用它。 - Aritra Roy
我已经添加了我的测试代码,对我来说,异步任务和通过Firebase的结果是相同的。 我不会同时运行它们。 - Benjamin Wulfe

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接