You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

无法将m4a文件上传至Firebase Storage,请求排查问题

Firebase Storage无法上传m4a文件排查与解决

问题概述

尝试将文件上传至Firebase Storage时,所有jpg文件均可成功上传,但m4a文件始终无法上传,且无任何日志或Toast提示信息。

参考截图

Firebase Storage上传异常截图

上传代码实现

Button uploadBtn = findViewById(R.id.uploadBtn);
uploadBtn.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View view) {
        File dir = new File(Environment.getExternalStorageDirectory() + "/Recordings");
        if (dir.exists()) {
            File[] files = dir.listFiles();
            for (File file : files) {
                // Skip if the file is a directory
                if (file.isDirectory()) {
                    continue;
                }

                // Check if file size is less than 5 MB
                double fileSizeInBytes = file.length();
                double fileSizeInMB = fileSizeInBytes / (1024 * 1024);
                if (fileSizeInMB < 5) {
                    // Upload file to Firebase Storage
                    Uri fileUri = Uri.fromFile(file);
                    final String filePath = fileUri.getPath();
                    FirebaseStorage storage = FirebaseStorage.getInstance();
                    StorageReference storageRef = storage.getReference();

                    final StorageReference recordingsRef = storageRef.child("Recordings/" + filePath);

                    // Check if the file already exists in the database
                    DatabaseReference dbRef = FirebaseDatabase.getInstance().getReference("Recordings");
                    dbRef.orderByChild("path").equalTo(filePath).addListenerForSingleValueEvent(new ValueEventListener() {
                        @Override
                        public void onDataChange(DataSnapshot dataSnapshot) {
                            if (!dataSnapshot.exists()) {
                                // File does not exist, upload it
                                StorageMetadata metadata = new StorageMetadata.Builder()
                                        .setContentType("application/octet-stream") // Set content type to generic binary data
                                        .build();
                                UploadTask uploadTask = recordingsRef.putFile(fileUri, metadata);

                                // Get the download URL of the uploaded file and save it to Firebase Realtime Database
                                uploadTask.continueWithTask(new Continuation<UploadTask.TaskSnapshot, Task<Uri>>() {
                                    @Override
                                    public Task<Uri> then(@NonNull Task<UploadTask.TaskSnapshot> task) throws Exception {
                                        if (!task.isSuccessful()) {
                                            throw task.getException();
                                        }
                                        return recordingsRef.getDownloadUrl();
                                    }
                                }).addOnCompleteListener(new OnCompleteListener<Uri>() {
                                    @Override
                                    public void onComplete(@NonNull Task<Uri> task) {
                                        if (task.isSuccessful()) {
                                            Uri downloadUri = task.getResult();
                                            String currentTime = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault()).format(new Date());
                                            Map<String, String> data = new HashMap<>();
                                            data.put("path", filePath);
                                            data.put("url", downloadUri.toString());
                                            data.put("time", currentTime);
                                            dbRef.push().setValue(data);
                                        } else {
                                            // Handle failures
                                            Toast.makeText(MainActivity.this, "" + task.getException(), Toast.LENGTH_SHORT).show();
                                        }
                                    }
                                });
                            }
                        }

                        @Override
                        public void onCancelled(DatabaseError databaseError) {
                            Toast.makeText(MainActivity.this, "" + databaseError.getDetails(), Toast.LENGTH_SHORT).show();
                        }
                    });
                }
            }
        }

    }
});

当前存储规则

rules_version = '2';
service firebase.storage {
  match /b/{bucket}/o {
    match /{allPaths=**} {
      allow read, write;
    }
  }
}

问题排查与解决建议

1. 存储路径构造错误

你构造recordingsRef时用了Recordings/加上fileUri.getPath(),fileUri.getPath()会返回设备绝对路径(如/storage/emulated/0/Recordings/xxx.m4a),最终存储路径会变成Recordings//storage/emulated/0/Recordings/xxx.m4a,这种多层嵌套的异常路径可能触发Firebase隐性限制,导致上传失败。

修复:只取文件名作为存储路径的一部分:

final String fileName = file.getName();
final StorageReference recordingsRef = storageRef.child("Recordings/" + fileName);

同时数据库中存储的path字段也改为fileName,避免路径冲突。

2. Content-Type设置不准确

application/octet-stream是通用二进制类型,但Firebase对媒体类型有更严格的校验逻辑,m4a属于MP4音频容器,应设置正确的MIME类型audio/mp4。可以根据文件后缀动态适配:

String contentType;
if (file.getName().toLowerCase().endsWith(".jpg") || file.getName().toLowerCase().endsWith(".jpeg")) {
    contentType = "image/jpeg";
} else if (file.getName().toLowerCase().endsWith(".m4a")) {
    contentType = "audio/mp4";
} else {
    contentType = "application/octet-stream";
}
StorageMetadata metadata = new StorageMetadata.Builder()
        .setContentType(contentType)
        .build();

3. 异常捕获不完整

当前仅在onComplete中捕获最终异常,上传过程中的中间错误可能被忽略。建议添加单独的失败监听,通过Logcat查看详细错误信息:

uploadTask.addOnFailureListener(new OnFailureListener() {
    @Override
    public void onFailure(@NonNull Exception e) {
        Log.e("StorageUpload", "上传失败:" + file.getName(), e);
        Toast.makeText(MainActivity.this, "文件" + file.getName() + "上传失败:" + e.getMessage(), Toast.LENGTH_LONG).show();
    }
});

4. 存储权限问题(Android 10+)

Android 10及以上版本限制了对外部存储的直接访问,虽然jpg能上传,但m4a文件可能存在读取权限缺失。建议:

  • 在AndroidManifest.xml的<application>标签中添加android:requestLegacyExternalStorage="true"(临时兼容方案)
  • 长期方案改用MediaStore或Scoped Storage获取文件Uri

5. 批量上传并发限制

循环发起多个上传任务可能触发Firebase的并发上传限制,导致部分任务被静默取消。可以改为串行上传,或用Executor控制并发数(如同时最多上传2个文件)。


内容的提问来源于stack exchange,提问作者Zibran

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.07 19:20:57