图片已上传Firebase Storage,元数据无法写入Realtime DB求助
图片上传Firebase Storage成功,但无法写入Realtime Database问题解决
问题描述
图片能正常上传到Firebase Storage,但图片名称和URL无法写入Firebase Realtime Database,这是项目推进的关键环节,急需解决。已参考同类问题方案但未成功,项目依赖配置正确,SHA-1/SHA-256已添加至Firebase控制台,对应配置文件也已更新。
原代码
package com.example.x_raydetect; import static android.content.ContentValues.TAG; import android.app.AlertDialog; import android.content.ContentResolver; import android.content.DialogInterface; import android.content.Intent; import android.graphics.Bitmap; import android.net.Uri; import android.os.Bundle; import android.os.Handler; import android.provider.MediaStore; import android.util.Log; import android.view.View; import android.webkit.MimeTypeMap; import android.widget.Button; import android.widget.EditText; import android.widget.ImageView; import android.widget.ProgressBar; import android.widget.Toast; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.widget.AlertDialogLayout; import com.google.android.gms.tasks.OnCanceledListener; import com.google.android.gms.tasks.OnCompleteListener; import com.google.android.gms.tasks.OnFailureListener; import com.google.android.gms.tasks.OnSuccessListener; import com.google.android.gms.tasks.Task; import com.google.android.gms.tasks.Tasks; import com.google.firebase.database.DatabaseReference; import com.google.firebase.database.FirebaseDatabase; import com.google.firebase.storage.FirebaseStorage; import com.google.firebase.storage.OnProgressListener; import com.google.firebase.storage.StorageReference; import com.google.firebase.storage.StorageTask; import com.google.firebase.storage.UploadTask; import com.squareup.picasso.Picasso; public class InsDB extends AppCompatActivity { private Button mainPage, btnView, btnMed, btnGallery, btnInsert, btnCamera; private EditText name; private ImageView insertImageView; private ProgressBar progressBar; public Uri imageUri; Bitmap bitmap; private StorageReference StorageRef; private DatabaseReference DatabaseRef; private StorageTask UploadTask; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_ins_database); //menu tabs mainPage = findViewById(R.id.mainPage); btnView = findViewById(R.id.btnView); btnMed = findViewById(R.id.btnMed); //interface buttons/GUY btnCamera = findViewById(R.id.btnCamera); btnGallery = findViewById(R.id.btnGallery); btnInsert = findViewById(R.id.btnInsert); insertImageView = findViewById(R.id.insert_ImageView); name = findViewById(R.id.name); progressBar = findViewById(R.id.progressBar); StorageRef = FirebaseStorage.getInstance().getReference("uploads"); DatabaseRef = FirebaseDatabase.getInstance("https://x-ray-detect-default-rtdb.asia-southeast1.firebasedatabase.app/").getReference("uploads"); mainPage.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { openMainPage(); } }); btnView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { openViewDB(); } }); btnMed.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { } }); btnGallery.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { openGalleryChooser(); } }); btnCamera.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); startActivityForResult(intent, 12); } }); btnInsert.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if (UploadTask != null && UploadTask.isInProgress()) { Toast.makeText(InsDB.this, "Upload in progress", Toast.LENGTH_SHORT).show(); } else { insertFile(); } } }); } private void openGalleryChooser() { Intent intent = new Intent(); intent.setAction(Intent.ACTION_GET_CONTENT); intent.setType("image/*"); startActivityForResult(intent, 10); } private String getFileExtension(Uri uri) { ContentResolver cR = getContentResolver(); MimeTypeMap mime = MimeTypeMap.getSingleton(); return mime.getExtensionFromMimeType(cR.getType(uri)); } private void insertFile() { if (imageUri != null) { StorageReference fileReference = StorageRef.child(System.currentTimeMillis() + "." + getFileExtension(imageUri)); UploadTask = fileReference.putFile(imageUri) .addOnSuccessListener(new OnSuccessListener<UploadTask.TaskSnapshot>() { @Override public void onSuccess(UploadTask.TaskSnapshot taskSnapshot) { Handler handler = new Handler(); handler.postDelayed(new Runnable() { @Override public void run() { progressBar.setProgress(0); } }, 500); Toast.makeText(InsDB.this, "Upload successful", Toast.LENGTH_LONG).show(); /* Upload upload = new Upload(name.getText().toString().trim(), taskSnapshot.getMetadata().getReference().getDownloadUrl().toString()); String uploadId = DatabaseRef.push().getKey(); DatabaseRef.child(uploadId).setValue(upload); */ Task<Uri> urlTask = taskSnapshot.getStorage().getDownloadUrl(); while (!urlTask.isSuccessful()); Uri downloadUrl = urlTask.getResult(); //Log.d(TAG, "onSuccess: firebase download url: " + downloadUrl.toString()); //use if testing...don't need this line. Upload upload = new Upload(name.getText().toString().trim(),downloadUrl.toString()); String uploadId = DatabaseRef.push().getKey(); DatabaseRef.child(uploadId).setValue(upload) //ADDED NEW LISTENERS// .addOnSuccessListener(new OnSuccessListener<Void>() { @Override public void onSuccess(Void unused) { new AlertDialog.Builder(InsDB.this).setTitle("Success") .setMessage(uploadId + name.getText().toString().trim() + downloadUrl.toString()) .setCancelable(true) .setPositiveButton("Yes", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { finish(); } }) .setNegativeButton("No", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { dialog.cancel(); } }) .show(); } }) .addOnFailureListener(new OnFailureListener() { @Override public void onFailure(@NonNull Exception e) { new AlertDialog.Builder(InsDB.this).setTitle("Failed") .setMessage(uploadId + name.getText().toString().trim() + downloadUrl.toString()) .setCancelable(true) .setPositiveButton("Yes", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { finish(); } }) .setNegativeButton("No", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { dialog.cancel(); } }) .show(); } }) .addOnCanceledListener(new OnCanceledListener() { @Override public void onCanceled() { new AlertDialog.Builder(InsDB.this) .setTitle("Cancelled") .setMessage(uploadId + name.getText().toString().trim() + downloadUrl.toString()) .setCancelable(true) .setPositiveButton("Yes", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { finish(); } }) .setNegativeButton("No", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { dialog.cancel(); } }) .show(); } }) .addOnCompleteListener(new OnCompleteListener<Void>() { @Override public void onComplete(@NonNull Task<Void> task) { new AlertDialog.Builder(InsDB.this) .setTitle("Complete") .setMessage(uploadId + name.getText().toString().trim() + downloadUrl.toString()) .setCancelable(true) .setPositiveButton("Yes", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { finish(); } }) .setNegativeButton("No", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { dialog.cancel(); } }) .show(); } }); } }) .addOnFailureListener(new OnFailureListener() { @Override public void onFailure(@NonNull Exception e) { Toast.makeText(InsDB.this, e.getMessage(), Toast.LENGTH_SHORT).show(); } }) .addOnProgressListener(new OnProgressListener<UploadTask.TaskSnapshot>() { @Override public void onProgress(UploadTask.TaskSnapshot snapshot) { double progress = (100.0 * snapshot.getBytesTransferred() / snapshot.getTotalByteCount()); progressBar.setProgress((int) progress); } }); } else { Toast.makeText(this, "No file selected", Toast.LENGTH_SHORT).show(); } } public void openMainPage() { Intent intent = new Intent(this, MainActivity.class); startActivity(intent); } public void openViewDB() { Intent intent = new Intent(this, ViewDB.class); startActivity(intent); } @Override protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) { super.onActivityResult(requestCode, resultCode, data); if (requestCode == 10 && data != null) { imageUri = data.getData(); Picasso.with(this).load(imageUri).into(insertImageView); } else if (requestCode == 12) { if (data != null) { bitmap = (Bitmap) data.getExtras().get("data"); insertImageView.setImageBitmap(bitmap); } } } }
问题排查与修复方案
1. 同步获取DownloadUrl的错误方式
原代码用while (!urlTask.isSuccessful());循环等待任务完成,会阻塞线程,且可能导致任务未完成就获取结果。需改为异步监听方式处理getDownloadUrl()。
2. Camera拍照后未设置imageUri
拍照返回Bitmap后,imageUri为空,insertFile()方法不会执行上传和数据库写入逻辑。需将Bitmap转换为Uri。
3. 数据库写入监听逻辑冗余
原代码同时添加了Success、Failure、Canceled、Complete监听,会弹出多个弹窗,需调整为只在最终状态提示。
修复后的核心代码修改
处理Camera拍照后的Uri转换
修改onActivityResult中拍照部分代码,并添加Bitmap转Uri方法:
else if (requestCode == 12) { if (data != null) { bitmap = (Bitmap) data.getExtras().get("data"); insertImageView.setImageBitmap(bitmap); // 将Bitmap转换为Uri imageUri = getImageUriFromBitmap(bitmap); } } // 添加Bitmap转Uri的方法 private Uri getImageUriFromBitmap(Bitmap bitmap) { ByteArrayOutputStream bytes = new ByteArrayOutputStream(); bitmap.compress(Bitmap.CompressFormat.JPEG, 100, bytes); String path = MediaStore.Images.Media.insertImage(getContentResolver(), bitmap, "X-Ray Image", null); return Uri.parse(path); }
修正DownloadUrl获取与数据库写入逻辑
修改insertFile()中的成功监听部分:
.addOnSuccessListener(new OnSuccessListener<UploadTask.TaskSnapshot>() { @Override public void onSuccess(UploadTask.TaskSnapshot taskSnapshot) { Handler handler = new Handler(); handler.postDelayed(() -> progressBar.setProgress(0), 500); Toast.makeText(InsDB.this, "上传成功", Toast.LENGTH_LONG).show(); // 异步获取DownloadUrl taskSnapshot.getStorage().getDownloadUrl().addOnSuccessListener(downloadUrl -> { String fileName = name.getText().toString().trim(); Upload upload = new Upload(fileName, downloadUrl.toString()); String uploadId = DatabaseRef.push().getKey(); // 写入数据库并处理结果 DatabaseRef.child(uploadId).setValue(upload) .addOnCompleteListener(task -> { if (task.isSuccessful()) { new AlertDialog.Builder(InsDB.this) .setTitle("操作成功") .setMessage("数据已写入数据库\nID: " + uploadId + "\n名称: " + fileName + "\nURL: " + downloadUrl) .setPositiveButton("确定", (dialog, which) -> dialog.dismiss()) .show(); } else { new AlertDialog.Builder(InsDB.this) .setTitle("操作失败") .setMessage("数据库写入失败: " + task.getException().getMessage()) .setPositiveButton("确定", (dialog, which) -> dialog.dismiss()) .show(); } }); }).addOnFailureListener(e -> { Toast.makeText(InsDB.this, "获取下载链接失败: " + e.getMessage(), Toast.LENGTH_SHORT).show(); }); } })
检查Upload类的序列化规则
确保Upload类符合Firebase Realtime Database要求:
public class Upload { public String name; public String url; // 必须包含无参构造函数 public Upload() {} public Upload(String name, String url) { this.name = name; this.url = url; } // 可选:添加Getter方法,提升兼容性 public String getName() { return name; } public String getUrl() { return url; } }
其他注意事项
- 检查Firebase Realtime Database规则,开发阶段可暂时设置为开放权限(上线前需修改为安全规则):
{ "rules": { ".read": true, ".write": true } } - 查看Logcat中
FirebaseDatabase标签的错误日志,排查权限或序列化问题。
内容的提问来源于stack exchange,提问作者SamanHuin
相关产品推荐
相关产品推荐

