شرح أساسيات Firebase Storage

التعامل مع Firebase Storage على الأندرويد ورفع وتحميل الملفات

AbdulAlim Rajjoubمنذ سنة

في هذا الدرس سنتحدث عن 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 عبر الرابط

كلمات دليلية: firebase storage android
3
إعجاب
2903
مشاهدات
0
مشاركة
0
متابع
متميز
محتوى رهيب

التعليقات (0)

لايوجد لديك حساب في عالم البرمجة؟

تحب تنضم لعالم البرمجة؟ وتنشئ عالمك الخاص، تنشر المقالات، الدورات، تشارك المبرمجين وتساعد الآخرين، اشترك الآن بخطوات يسيرة !