شرح أساسيات Firebase Storage
التعامل مع Firebase Storage على الأندرويد ورفع وتحميل الملفات
في هذا الدرس سنتحدث عن Firebase Storage وهي وسيلة لتخزين البيانات ضمن بيئة Firebase ويمكنك تخزين أي نوع من الملفات داخلها.
نبدأ بربط مشروع الأندرويد بمشروع Firebase (يمكنك مراجعة هذا الدرس لمعرفة كيفية ربط المشروع)
بعد ذلك نقوم بإضافة مكتبة Firebase Storage
implementation 'com.google.firebase:firebase-storage:11.8.0'
ثم نقوم بعمل Sync لبدء تحميل المكتبة
الأساسيات
سنبدأ الآن بشرح مثال بسيط عن كيفية رفع صورة وثم تحميلها مرة أخرى, ثم في الدرس القادم سنقوم بعمل تطبيق بسيط وفكرته تخزين صورك الشخصية وعرضها في التطبيق بالتعاون مع قاعدة البيانات Firebase Realtime Database
بدايةً سنقوم بتعريف Instance من Firebase Storage
اذا كنت قد تعاملت مع Firebase Realtime Database فستجد أنهم يستخدمون نفس التنسيق بالنسبة لInstance و Reference
FirebaseStorage firebaseStorage = FirebaseStorage.getInstance();
ثم نقوم بأخذ Reference وسيكون Main Reference ,بمعنى أنه اذا رفعنا صورة ما فسيتم رفعها على المجلد الجذر على الFirebase Storage كما سنرى لاحقاً
StorageReference mainRef = firebaseStorage.getReference();
الآن سنبدأ برفع صورة الى Firebase Storage ,ولهذا سنقوم بجعل المستخدم يختار صورة من المعرض
سنقوم بإضافة زر وعند الضغط عليه سنقوم باختيار صورة من المعرض ثم رفعها الى Firebase Storage
سنقوم بإنشاء Intent لفتح المعرض ونريد فقط اختيار الصور حالياً وسنستلم نتيجة الإختيار في onActivityResult
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent photoPickerIntent = new Intent(Intent.ACTION_PICK);
photoPickerIntent.setType("image/*");
startActivityForResult(photoPickerIntent, PICK_IMG_REQUEST/* dummy number*/);
}
});
الآن نستلم نتيجة اختيار الصورة عبر onActivityResult
اذا كانت resultCode == RESULT_OK أي أن المستخدم قد اختار صورة ما ونريد وسنأخذ مسار الصورة (الuri ) الذي سنعطيه ل Firebase Storage عند الرفع ,أما اذا لم يتم اختيار صورة سنقوم بإظهار رسالة توست
@Override
protected void onActivityResult(int reqCode, int resultCode, Intent data) {
super.onActivityResult(reqCode, resultCode, data);
if (resultCode == RESULT_OK) {
final Uri imageUri = data.getData();
} else {
Toast.makeText(this, "no image selected :/", Toast.LENGTH_SHORT).show();
}
}
الآن لنجرب عملية اختيار الصورة ونقوم بعمل log لuri
اذا تمت عملية بنجاح فسترى مثل هذا uri في LogCat
الآن نقوم بإنشاء ميثود upload التى ستقوم برفع الصورة الى Firebase Storage وتأخذ Uri كبارامتر
ونقوم بتوليد اسم عشوائي للصورة ونضيف اللاحقة jpg
ثم نستدعي mainRef الذي أنشأناه سابقاً ونستخدم ميثود ثم .child التي تقوم بأخذ اسم الصورة لإنشاء الملف عند الرفع
ثم نستخدم putFile التي تأخذ uri كبارامتر ونقوم بإضافة OnCompleteListener الذي سيعلمنا عند انتهاء عملية الرفع ويعيد Task والتي تحتوي على الكثير من المعلومات المفيدة التي سنستعملها لاحقاً..
private void upload(Uri uri) {
String imageName = UUID.randomUUID().toString() + ".jpg";
mainRef.child(imageName).putFile(uri).addOnCompleteListener(new OnCompleteListener<UploadTask.TaskSnapshot>() {
@Override
public void onComplete(@NonNull Task<UploadTask.TaskSnapshot> task) {
}
});
}
}
الآن نقوم بالتحقق اذا تمت عملية الرفع بنجاح ونقوم بعمل Toast وإلا سنقوم بطباعة الخطأ في LogCat
if (task.isSuccessful()) {
Toast.makeText(MainActivity.this, "Uplaod Succeed", Toast.LENGTH_SHORT).show();
} else {
Log.d("3llomi", "upload Failed " + task.getException().getLocalizedMessage());
Toast.makeText(MainActivity.this, "Uplaod Failed :( " + task.getException().getLocalizedMessage(), Toast.LENGTH_LONG).show();
}
أخيراً نستدعي هذه الميثود في onActivityResult
@Override
protected void onActivityResult(int reqCode, int resultCode, Intent data) {
super.onActivityResult(reqCode, resultCode, data);
if (resultCode == RESULT_OK) {
final Uri imageUri = data.getData();
Log.d("3llomi","IMAGE URI is "+ imageUri);
upload(imageUri);
} else {
Toast.makeText(this, "no image selected :/", Toast.LENGTH_SHORT).show();
}
}
نقوم بتشغيل التطبيق ونجرب اختيار صورة ورفعها ونتوجه الى LogCat لنرى نتيجة الرفع
وكما نرى الحياة مليئة بالأخطاء البرمجية :/
وسبب هذا الخطأ هو إعدادات الأمان في Firebase والتي افتراضياً تقوم بمنع أي شخص من رفع او تحميل الصور في حال لم يقم بتسجيل الدخول
ولهذا سنتوجه الى Firebase Console الى خيار Storage ثم Rules
افتراضياً يكون بهذا الشكل
نقوم بتغييره الى
ونقوم بالضغط على Publish لحفظ التغييرات
ملاحظة :لا يفضل تفعيل هذا الخيار لعدم وجود أي أمان على بياناتك ,ولكن في سبيل التجربة فلا مشكلة
نعود الى التطبيق ونحاول اعادة رفع الصورة وعند نجاح العملية سيظهر التوست,وللتأكد نذهب الى Firebase Console وسنجد هذه الصورة الجميلة :D
ملاحظة:اذا أردت رفع ملف ما من الذاكرة فيجب عليك تحويل مسار الملف الى Uri بهذا الشكل(تأكد من إعطاء الصلاحيات READ_EXTERNAL_STORAGE)
File file = new File(Environment.getExternalStorageDirectory() + "/" + "image.jpg");
Uri filePath = Uri.fromFile(file);
mainRef.child(imageName).putFile(filePath);
.........
تحميل الملفات
الآن سنتجه الى تحميل الملفات وسنقوم بتحميل نفس الصورة بعد رفعها على سبيل التجربة
سنقوم بإنشاء ميثود Download وتأخذ String اسم الصورة كبارامتر
ونقوم بإنشاء File وهو المسار الذي سيتم حفظ الصورة فيه ,سنقوم بتخزين الصورة ضمن مسار التطبيق
ثم نستخدم الميثود getFile لبدء تحميل الملف وهي تأخذ اما uri او file كبارامتر
ونقوم بتطبيق نفس الأمور عند نجاح التحميل او فشله
private void download(String imageName) {
final File file = new File(getFilesDir(), imageName);
mainRef.child(imageName).getFile(file).addOnCompleteListener(new OnCompleteListener<FileDownloadTask.TaskSnapshot>() {
@Override
public void onComplete(@NonNull Task<FileDownloadTask.TaskSnapshot> task) {
if (task.isSuccessful()) {
Toast.makeText(MainActivity.this, "file Downloaded to " + file.getPath(), Toast.LENGTH_SHORT).show();
Log.d("3llomi", "File Downloaded to " + file.getPath());
} else {
Toast.makeText(MainActivity.this, "download Failed " + task.getException().getLocalizedMessage(), Toast.LENGTH_SHORT).show();
Log.d("3llomi", "Download Failed " + task.getException().getLocalizedMessage());
}
}
});
}
أخيراً نقوم باستدعاء هذه الميثود داخل الميثود upload عند نجاح عملية الرفع
private void upload(Uri uri) {
final String imageName = UUID.randomUUID().toString() + ".jpg";
mainRef.child(imageName).putFile(uri).addOnCompleteListener(new OnCompleteListener<UploadTask.TaskSnapshot>() {
@Override
public void onComplete(@NonNull Task<UploadTask.TaskSnapshot> task) {
if (task.isSuccessful()) {
download(imageName);
Toast.makeText(MainActivity.this, "Uplaod Succeed", Toast.LENGTH_SHORT).show();
} else {
Log.d("3llomi", "upload Failed " + task.getException().getLocalizedMessage());
Toast.makeText(MainActivity.this, "Uplaod Failed :( " + task.getException().getLocalizedMessage(), Toast.LENGTH_LONG).show();
}
}
});
}
أخيراً نقوم بتشغيل التطبيق لنرى النتائج
وعند نجاح عملية الرفع ثم التحميل سنرى نتيجة التحميل والمسار الذي تم حفظ الصورة فيه
إظهار Progress Bar عند التحميل أو الرفع
Firebase Storage تقدم لنا الميثود
addOnProgressListener()
التي تعيد لنا عدد البايتات التي تم تحميلها بالإضافة الى عدد بايتات الملف الكلي
ثم نقوم بحساب النسبة عبر ضرب عدد البايتات المحملة ب100 ثم تقسيمها على عدد البايتات الكلي
.addOnProgressListener(new OnProgressListener<FileDownloadTask.TaskSnapshot>() { @Override public void onProgress(FileDownloadTask.TaskSnapshot taskSnapshot) { double progressDouble = (100.0 * taskSnapshot.getBytesTransferred()) / taskSnapshot.getTotalByteCount(); } });
الآن سننشأ ProgressDialog في بداية الميثود Download على سبيل المثال (نفس الكلام ينطبق على upload)
ونقوم بعمل dismiss ل dialog عند الانتهاء ونقوم بعمل setProgress في onProgress
لتصبح بهذا الشكل
private void download(String imageName) {
final ProgressDialog progressDialog = new ProgressDialog(this);
progressDialog.setMessage("Downloading...");
progressDialog.show();
final File file = new File(getFilesDir(), imageName);
mainRef.child(imageName).getFile(file).addOnCompleteListener(new OnCompleteListener<FileDownloadTask.TaskSnapshot>() {
@Override
public void onComplete(@NonNull Task<FileDownloadTask.TaskSnapshot> task) {
progressDialog.dismiss();
if (task.isSuccessful()) {
Toast.makeText(MainActivity.this, "file Downloaded to " + file.getPath(), Toast.LENGTH_SHORT).show();
Log.d("3llomi", "File Downloaded to " + file.getPath());
} else {
Toast.makeText(MainActivity.this, "download Failed " + task.getException().getLocalizedMessage(), Toast.LENGTH_SHORT).show();
Log.d("3llomi", "Download Failed " + task.getException().getLocalizedMessage());
}
}
}).addOnProgressListener(new OnProgressListener<FileDownloadTask.TaskSnapshot>() {
@Override
public void onProgress(FileDownloadTask.TaskSnapshot taskSnapshot) {
double progressDouble = (100.0 * taskSnapshot.getBytesTransferred()) / taskSnapshot.getTotalByteCount();
progressDialog.setMessage(progressDouble+"");
}
});
}
إيقاف عملية التحميل او الرفع
في بعض الأحيان نحتاج الى إيقاف عملية التحميل او الرفع ولهذا تقدم لنا FirebaseStorage الميثود cancel()
ولكن يجب علينا أن نقوم بتعريف عملية التحميل أو الرفع ,سابقاً كنا نستخدم Anonymous Method لأننا كنا لانحتاج هذه Task
ولهذا نقوم بتعريف Task
بالنسبة ل عملية الرفع فستكون بهذا الشكل
UploadTask uploadTask = mainRef.child(imageName).putFile(uri);
uploadTask.addOnCompleteListener(.....)
ولإيقاف عملية الرفع نستخدم الميثود cancel()
uploadTask.cancel();
أما بالنسبة لعملية التحميل
FileDownloadTask fileDownloadTask = mainRef.child(imageName).getFile(file); fileDownloadTask.addOnCompleteListener(.....)
fileDownloadTask.cancel();
الكود كاملاً لهذا الدرس
public class MainActivity extends AppCompatActivity {
FirebaseStorage firebaseStorage;
StorageReference mainRef;
public static final int PICK_IMG_REQUEST = 6541;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
firebaseStorage = FirebaseStorage.getInstance();
mainRef = firebaseStorage.getReference();
Button button = findViewById(R.id.upload_btn);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent photoPickerIntent = new Intent(Intent.ACTION_PICK);
photoPickerIntent.setType("image/*");
startActivityForResult(photoPickerIntent, PICK_IMG_REQUEST);
}
});
}
@Override
protected void onActivityResult(int reqCode, int resultCode, Intent data) {
super.onActivityResult(reqCode, resultCode, data);
if (resultCode == RESULT_OK) {
final Uri imageUri = data.getData();
Log.d("3llomi", "IMAGE URI is " + imageUri);
upload(imageUri);
} else {
Toast.makeText(this, "no image selected :/", Toast.LENGTH_SHORT).show();
}
}
private void upload(Uri uri) {
final String imageName = UUID.randomUUID().toString() + ".jpg";
UploadTask uploadTask = mainRef.child(imageName).putFile(uri);
uploadTask.addOnCompleteListener(new OnCompleteListener<UploadTask.TaskSnapshot>() {
@Override
public void onComplete(@NonNull Task<UploadTask.TaskSnapshot> task) {
if (task.isSuccessful()) {
download(imageName);
Toast.makeText(MainActivity.this, "Uplaod Succeed", Toast.LENGTH_SHORT).show();
} else {
Log.d("3llomi", "upload Failed " + task.getException().getLocalizedMessage());
Toast.makeText(MainActivity.this, "Uplaod Failed :( " + task.getException().getLocalizedMessage(), Toast.LENGTH_LONG).show();
}
}
});
//if you want to cancel
//uploadTask.cancel();
}
private void download(String imageName) {
final ProgressDialog progressDialog = new ProgressDialog(this);
progressDialog.setMessage("Downloading...");
progressDialog.show();
final File file = new File(getFilesDir(), imageName);
FileDownloadTask fileDownloadTask = mainRef.child(imageName).getFile(file);
fileDownloadTask.addOnCompleteListener(new OnCompleteListener<FileDownloadTask.TaskSnapshot>() {
@Override
public void onComplete(@NonNull Task<FileDownloadTask.TaskSnapshot> task) {
progressDialog.dismiss();
if (task.isSuccessful()) {
Toast.makeText(MainActivity.this, "file Downloaded to " + file.getPath(), Toast.LENGTH_SHORT).show();
Log.d("3llomi", "File Downloaded to " + file.getPath());
} else {
Toast.makeText(MainActivity.this, "download Failed " + task.getException().getLocalizedMessage(), Toast.LENGTH_SHORT).show();
Log.d("3llomi", "Download Failed " + task.getException().getLocalizedMessage());
}
}
}).addOnProgressListener(new OnProgressListener<FileDownloadTask.TaskSnapshot>() {
@Override
public void onProgress(FileDownloadTask.TaskSnapshot taskSnapshot) {
double progressDouble = (100.0 * taskSnapshot.getBytesTransferred()) / taskSnapshot.getTotalByteCount();
progressDialog.setMessage(progressDouble + "");
}
});
//if you want to cancel
//fileDownloadTask.cancel();
}
}
يمكنك الإطلاع على الحدود الخاصة بالمساحة بFirebase Storage عبر الرابط
لايوجد لديك حساب في عالم البرمجة؟
تحب تنضم لعالم البرمجة؟ وتنشئ عالمك الخاص، تنشر المقالات، الدورات، تشارك المبرمجين وتساعد الآخرين، اشترك الآن بخطوات يسيرة !