为什么图片上传到Firebase Storage失败且应用发生崩溃?
问题根因
- 核心错误是Firebase Cloud Storage权限配置错误:崩溃日志明确返回403 Permission denied,说明应用当前没有权限向Storage的images路径上传文件。
- 附加代码逻辑缺陷:
- 点击保存按钮后立刻调用
finish()关闭页面,此时图片上传是异步操作,页面销毁会导致后续回调无法正常执行,甚至触发空指针崩溃 uploadImage()方法没有判断imageUri是否为null就直接调用putFile(),如果用户没选图片就提交会直接崩溃- 用
while (!uriTask.isSuccessful());死循环等待下载地址,属于错误写法,会阻塞主线程 - 上传失败的
onFailureListener是空实现,没有错误捕获逻辑,异常直接抛出导致崩溃
- 点击保存按钮后立刻调用
修复步骤
第一步:修复Firebase Storage权限
打开Firebase控制台,进入对应项目 -> 存储 -> 规则,默认规则禁止未认证用户读写,如果你还未接入用户认证,可以先临时改为测试规则(注意生产环境要替换为符合业务的权限控制,建议结合Firebase Auth做身份校验后再开放对应路径读写权限,避免数据泄露):
rules_version = '2'; service firebase.storage { match /b/{bucket}/o { match /{allPaths=**} { allow read, write: if true; } } }
发布规则后等待2分钟生效即可。
第二步:修复代码逻辑缺陷
1. 调整保存按钮逻辑,不要立刻关闭页面
修改onOptionsItemSelected中的保存逻辑,移除立刻调用的finish(),改为在所有数据写入成功后再关闭页面:
public boolean onOptionsItemSelected(MenuItem item) { if (item.getItemId() == R.id.action_save) { insertPerson(); // 移除此处的finish(),改到sendList成功回调后执行 return true; } // 其余原有代码保持不变 }
2. 增加imageUri非空判断
修改insertPerson方法,新增图片选择校验:
private void insertPerson() { // 原有字段校验逻辑保持不变 // 新增图片校验 if (imageUri == null) { Toast.makeText(this, "请先选择人员照片", Toast.LENGTH_SHORT).show(); return; } uploadImage(); }
3. 修复下载地址获取逻辑,替换死循环写法
private void uploadImage() { long timestamp = System.currentTimeMillis(); String filePathAndName = "images/" + timestamp; StorageReference storageReference = FirebaseStorage.getInstance().getReference(filePathAndName); storageReference.putFile(imageUri) .addOnSuccessListener(taskSnapshot -> { // 替换死循环,用异步回调获取下载地址 taskSnapshot.getStorage().getDownloadUrl().addOnSuccessListener(uri -> { String uploadedImageUri = uri.toString(); sendList(uploadedImageUri, timestamp); }).addOnFailureListener(e -> { Toast.makeText(AddPerson.this, "获取图片地址失败:" + e.getMessage(), Toast.LENGTH_SHORT).show(); }); }).addOnFailureListener(e -> { // 新增错误提示 Toast.makeText(AddPerson.this, "图片上传失败:" + e.getMessage(), Toast.LENGTH_SHORT).show(); }); }
4. 数据库写入成功后再关闭页面
修改sendList方法,增加写入结果监听:
private void sendList(String uploadedImageUri, long timestamp) { DatabaseReference reference = FirebaseDatabase.getInstance().getReference("List"); HashMap<String, Object> hashMap = new HashMap<>(); hashMap.put("firstName", firstNameString); hashMap.put("lastName", lastNameString); hashMap.put("age", ageString); hashMap.put("description", descriptionString); hashMap.put("url", uploadedImageUri); hashMap.put("timestamp", timestamp); reference.push().setValue(hashMap) .addOnSuccessListener(aVoid -> { Toast.makeText(AddPerson.this, "保存成功", Toast.LENGTH_SHORT).show(); finish(); }) .addOnFailureListener(e -> { Toast.makeText(AddPerson.this, "数据保存失败:" + e.getMessage(), Toast.LENGTH_SHORT).show(); }); }
内容的提问来源于stack exchange,提问作者user17093321
相关产品推荐
相关产品推荐

