كيفية استخدام App Engine blobstore (الوحدة 15)

1. نظرة عامة

تهدف سلسلة البرامج التعليمية حول Serverless Migration Station (برامج تعليمية ذاتية السرعة وعملية) ومقاطع الفيديو ذات الصلة إلى مساعدة مطوّري الحوسبة بدون خادم على Google Cloud في تحديث تطبيقاتهم من خلال إرشادهم خلال عملية نقل واحدة أو أكثر، مع التركيز بشكل أساسي على التخلّي عن الخدمات القديمة. يؤدي ذلك إلى زيادة قابلية نقل تطبيقاتك ويمنحك المزيد من الخيارات والمرونة، ما يتيح لك الدمج مع مجموعة أكبر من منتجات Cloud والوصول إليها، كما يسهّل عليك الترقية إلى أحدث إصدارات اللغة. على الرغم من أنّ هذه السلسلة تركّز في البداية على أوائل مستخدمي Cloud، وخاصةً مطوّري App Engine (البيئة العادية)، إلا أنّها واسعة النطاق بما يكفي لتشمل منصات أخرى بلا خادم، مثل Cloud Functions وCloud Run، أو في أي مكان آخر إذا كان ذلك منطبقًا.

يوضّح هذا الدرس التطبيقي حول الترميز في الوحدة 15 كيفية إضافة استخدام App Engine blobstore إلى نموذج التطبيق من الوحدة 0. بعد ذلك، ستكون مستعدًا لنقل هذا الاستخدام إلى Cloud Storage في الوحدة 16.

ستتعرَّف على كيفية إجراء ما يلي:

  • إضافة استخدام واجهة برمجة التطبيقات/المكتبة App Engine Blobstore
  • تخزين عمليات التحميل التي يجريها المستخدم في خدمة blobstore
  • الاستعداد للخطوة التالية لنقل البيانات إلى Cloud Storage

المتطلبات

استطلاع الرأي

كيف ستستخدم هذا البرنامج التعليمي؟

قراءة المحتوى فقط قراءة المحتوى وإكمال التمارين

كيف تقيّم تجربتك مع Python؟

مبتدئ متوسط متمكّن

ما هو تقييمك لتجربة استخدام خدمات Google Cloud؟

مبتدئ متوسط متقدّم

2. الخلفية

لإجراء نقل بيانات من App Engine Blobstore API، أضِف استخدامها إلى تطبيق App Engine الأساسي الحالي ndb من الوحدة 0. يعرض نموذج التطبيق آخر عشر زيارات للمستخدم. نحن بصدد تعديل التطبيق لكي يطلب من المستخدم النهائي تحميل عنصر (ملف) يتوافق مع "زيارته". إذا لم يرِد المستخدم إجراء ذلك، يتوفّر خيار "تخطّي". بغض النظر عن قرار المستخدم، تعرض الصفحة التالية الناتج نفسه الذي يعرضه التطبيق من الوحدة 0 (والعديد من الوحدات الأخرى في هذه السلسلة). بعد تنفيذ عملية دمج blobstore في App Engine، يمكننا نقلها إلى Cloud Storage في الدرس التطبيقي التالي حول الترميز (الوحدة 16).

توفّر App Engine إمكانية الوصول إلى نظامَي النماذج Django وJinja2، وأحد الاختلافات في هذا المثال (بالإضافة إلى إضافة إمكانية الوصول إلى Blobstore) هو أنّه ينتقل من استخدام Django في الوحدة 0 إلى Jinja2 هنا في الوحدة 15. تتمثّل إحدى الخطوات الأساسية في تطوير تطبيقات App Engine في نقل أُطر عمل الويب من webapp2 إلى Flask. يستخدم هذا الأخير Jinja2 كنظام نماذج تلقائي، لذا سنبدأ في التوجّه نحو ذلك من خلال تنفيذ Jinja2 مع البقاء على webapp2 للوصول إلى Blobstore. بما أنّ Flask يستخدم Jinja2 تلقائيًا، هذا يعني أنّه لن تكون هناك حاجة إلى إجراء أي تغييرات على النموذج في الوحدة 16.

3- الإعداد/العمل التحضيري

قبل الانتقال إلى الجزء الرئيسي من البرنامج التعليمي، عليك إعداد مشروعك والحصول على الرمز البرمجي ونشر التطبيق الأساسي للبدء في استخدام الرمز البرمجي.

1. إعداد المشروع

إذا سبق لك نشر تطبيق الوحدة 0، ننصحك بإعادة استخدام المشروع (والرمز) نفسه. يمكنك بدلاً من ذلك إنشاء مشروع جديد تمامًا أو إعادة استخدام مشروع حالي آخر. تأكَّد من أنّ المشروع يتضمّن حساب فوترة نشطًا وأنّ خدمة App Engine مفعَّلة.

2. الحصول على نموذج تطبيق أساسي

من المتطلبات الأساسية لهذا الدرس التطبيقي حول الترميز توفُّر نموذج تطبيق يعمل في الوحدة 0. وإذا لم يكن لديك هذا النموذج، يمكنك الحصول عليه من مجلد "البدء" في الوحدة 0 (الرابط أدناه). يرشدك هذا الدرس العملي خلال كل خطوة، وينتهي برمز يشبه الرمز الموجود في المجلد "FINISH" في الوحدة 15.

يجب أن يبدو دليل ملفات Module 0 START على النحو التالي:

$ ls
README.md               index.html
app.yaml                main.py

3- (إعادة) نشر التطبيق الأساسي

في ما يلي الخطوات المتبقية التي يجب تنفيذها الآن:

  1. التعرّف من جديد على أداة سطر الأوامر gcloud
  2. إعادة نشر نموذج التطبيق باستخدام gcloud app deploy
  3. تأكَّد من أنّ التطبيق يعمل على App Engine بدون أي مشاكل

بعد تنفيذ هذه الخطوات بنجاح والتأكّد من أنّ تطبيق الويب يعمل (مع إخراج مشابه لما يلي)، ستكون جاهزًا لإضافة استخدام التخزين المؤقت إلى تطبيقك.

a7a9d2b80d706a2b.png

4. تعديل ملفات الإعداد

app.yaml

لا توجد تغييرات جوهرية في إعدادات التطبيق، ولكن كما ذكرنا سابقًا، سننتقل من استخدام قوالب Django (الإعداد التلقائي) إلى Jinja2، لذا يجب أن يحدّد المستخدمون أحدث إصدار من Jinja2 متاح على خوادم App Engine من أجل التبديل، ويمكنك إجراء ذلك عن طريق إضافته إلى قسم مكتبات الجهات الخارجية المضمّنة في app.yaml.

قبل:

runtime: python27
threadsafe: yes
api_version: 1

handlers:
- url: /.*
  script: main.app

عدِّل ملف app.yaml من خلال إضافة قسم libraries جديد كما هو موضّح هنا:

بعد:

runtime: python27
threadsafe: yes
api_version: 1

handlers:
- url: /.*
  script: main.app

libraries:
- name: jinja2
  version: latest

لا حاجة إلى تعديل أي ملفات إعداد أخرى، لذا لننتقل إلى ملفات التطبيق.

5- تعديل ملفات التطبيق

عمليات الاستيراد والتوافق مع Jinja2

تتضمّن المجموعة الأولى من التغييرات في main.py إضافة استخدام Blobstore API واستبدال قوالب Django بقوالب Jinja2. في ما يلي التغييرات التي سيتم إجراؤها:

  1. الغرض من الوحدة os هو إنشاء اسم مسار ملف لنموذج Django. بما أنّنا سننتقل إلى Jinja2 حيث يتم التعامل مع هذا الأمر، لن يعود من الضروري استخدام os بالإضافة إلى أداة عرض نماذج Django، أي google.appengine.ext.webapp.template، لذا سيتم إزالتهما.
  2. استورِد Blobstore API: google.appengine.ext.blobstore
  3. استورِد معالِجات Blobstore المتوفّرة في إطار عمل webapp الأصلي، فهي غير متاحة في webapp2: google.appengine.ext.webapp.blobstore_handlers
  4. استيراد دعم Jinja2 من حزمة webapp2_extras

قبل:

import os
import webapp2
from google.appengine.ext import ndb
from google.appengine.ext.webapp import template

طبِّق التغييرات الواردة في القائمة أعلاه عن طريق استبدال قسم الاستيراد الحالي في main.py بمقتطف الرمز البرمجي أدناه.

بعد:

import webapp2
from webapp2_extras import jinja2
from google.appengine.ext import blobstore, ndb
from google.appengine.ext.webapp import blobstore_handlers

بعد عمليات الاستيراد، أضِف بعض رمز النص النموذجي لدعم استخدام Jinja2 كما هو محدّد في مستندات webapp2_extras. يغلّف مقتطف الرمز التالي فئة معالج طلب webapp2 العادية بوظيفة Jinja2، لذا أضِف مجموعة الرموز هذه إلى main.py بعد عمليات الاستيراد مباشرةً:

class BaseHandler(webapp2.RequestHandler):
    'Derived request handler mixing-in Jinja2 support'
    @webapp2.cached_property
    def jinja2(self):
        return jinja2.get_jinja2(app=self.app)

    def render_response(self, _template, **context):
        self.response.write(self.jinja2.render_template(_template, **context))

إضافة دعم Blobstore

على عكس عمليات نقل البيانات الأخرى في هذه السلسلة التي نحافظ فيها على وظائف نموذج التطبيق أو الناتج مطابقًا (أو متطابقًا تقريبًا) بدون تغيير (كبير) في تجربة المستخدم، يختلف هذا المثال بشكل كبير عن المعتاد. بدلاً من تسجيل زيارة جديدة على الفور ثم عرض آخر عشر زيارات، نعمل على تعديل التطبيق ليطلب من المستخدم تقديم قطعة أثرية من الملف لتسجيل زيارته. يمكن للمستخدمين النهائيين بعد ذلك تحميل ملف مطابق أو النقر على "تخطّي" لعدم تحميل أي ملف على الإطلاق. بعد إكمال هذه الخطوة، ستظهر صفحة "أحدث الزيارات".

يسمح هذا التغيير لتطبيقنا باستخدام خدمة Blobstore لتخزين هذه الصورة أو نوع الملف الآخر (وربما عرضها لاحقًا) في صفحة "عمليات البحث الأخيرة".

تعديل نموذج البيانات وتنفيذ استخدامه

نخزِّن المزيد من البيانات، وتحديدًا نعدّل نموذج البيانات لتخزين رقم التعريف (المسمّى "BlobKey") للملف الذي تم تحميله إلى Blobstore، ونضيف مرجعًا لحفظه في store_visit(). بما أنّ هذه البيانات الإضافية يتم عرضها مع كل البيانات الأخرى عند إجراء طلب بحث، يظل fetch_visits() كما هو.

في ما يلي مقارنة بين الشكل السابق والجديد لهذه التحديثات التي تتضمّن file_blob، وهو ndb.BlobKeyProperty:

قبل:

class Visit(ndb.Model):
    'Visit entity registers visitor IP address & timestamp'
    visitor   = ndb.StringProperty()
    timestamp = ndb.DateTimeProperty(auto_now_add=True)

def store_visit(remote_addr, user_agent):
    'create new Visit entity in Datastore'
    Visit(visitor='{}: {}'.format(remote_addr, user_agent)).put()

def fetch_visits(limit):
    'get most recent visits'
    return Visit.query().order(-Visit.timestamp).fetch(limit)

بعد:

class Visit(ndb.Model):
    'Visit entity registers visitor IP address & timestamp'
    visitor   = ndb.StringProperty()
    timestamp = ndb.DateTimeProperty(auto_now_add=True)
    file_blob = ndb.BlobKeyProperty()

def store_visit(remote_addr, user_agent, upload_key):
    'create new Visit entity in Datastore'
    Visit(visitor='{}: {}'.format(remote_addr, user_agent),
            file_blob=upload_key).put()

def fetch_visits(limit):
    'get most recent visits'
    return Visit.query().order(-Visit.timestamp).fetch(limit)

في ما يلي تمثيل صوري للتغييرات التي تم إجراؤها حتى الآن:

2270783776759f7f.png

إتاحة تحميل الملفات

أهم تغيير في الوظائف هو إتاحة تحميل الملفات، سواء من خلال مطالبة المستخدم بملف أو إتاحة ميزة "تخطّي" أو عرض ملف يتوافق مع زيارة. كل ذلك جزء من الصورة. في ما يلي التغييرات المطلوبة لإتاحة تحميل الملفات:

  1. لم يعُد معالج GET الطلبات الرئيسي يجلب أحدث الزيارات لعرضها. بدلاً من ذلك، يطلب من المستخدم تحميل ملف.
  2. عندما يرسل مستخدم نهائي ملفًا لتحميله أو يتخطّى هذه العملية، يمرّر POST من النموذج عنصر التحكّم إلى UploadHandler الجديد، المستمد من google.appengine.ext.webapp.blobstore_handlers.BlobstoreUploadHandler.
  3. تنفّذ طريقة UploadHandler عملية التحميل، وتطلب POST لتسجيل الزيارة، وتؤدي إلى إعادة توجيه HTTP 307 لإعادة المستخدم إلى "/"، حيث...store_visit()
  4. تستعلم طريقة POST في المعالج الرئيسي عن أحدث الزيارات (من خلال fetch_visits()) وتعرضها. إذا اختار المستخدم "تخطّي"، لن يتم تحميل أي ملف، ولكن سيتم تسجيل الزيارة مع إعادة التوجيه نفسها.
  5. يتضمّن عرض الزيارات الأخيرة حقلاً جديدًا يظهر للمستخدم، إما "عرض" مرتبطًا بتشعّب إذا كان ملف التحميل متاحًا أو "لا شيء" في حال عدم توفّره. تظهر هذه التغييرات في نموذج HTML بالإضافة إلى إضافة نموذج تحميل (سيتم توفير المزيد من المعلومات حول هذا الموضوع قريبًا).
  6. إذا نقر أحد المستخدِمين النهائيين على رابط "عرض" لأي زيارة تتضمّن فيديو تم تحميله، سيتم إرسال طلب GET إلى ViewBlobHandler جديد مشتق من google.appengine.ext.webapp.blobstore_handlers.BlobstoreDownloadHandler، إما لعرض الملف إذا كانت صورة (في المتصفّح إذا كان متوافقًا)، أو لطلب تنزيله إذا لم يكن متوافقًا، أو لعرض الخطأ HTTP 404 إذا لم يتم العثور عليه.
  7. بالإضافة إلى فئتَي المعالجة الجديدتَين ومجموعة المسارات الجديدة لإرسال الزيارات إليهما، تحتاج المعالجة الرئيسية إلى طريقة POST جديدة لتلقّي عملية إعادة التوجيه 307 الموضّحة أعلاه.

قبل هذه التحديثات، كان تطبيق الوحدة 0 يتضمّن معالجًا رئيسيًا فقط مع طريقة GET ومسارًا واحدًا:

قبل:

class MainHandler(webapp2.RequestHandler):
    'main application (GET) handler'
    def get(self):
        store_visit(self.request.remote_addr, self.request.user_agent)
        visits = fetch_visits(10)
        tmpl = os.path.join(os.path.dirname(__file__), 'index.html')
        self.response.out.write(template.render(tmpl, {'visits': visits}))

app = webapp2.WSGIApplication([
    ('/', MainHandler),
], debug=True)

بعد تنفيذ هذه التعديلات، أصبح هناك ثلاثة معالِجات: 1) معالج تحميل يتضمّن الطريقة POST، و2) معالج تنزيل "عرض كائن ثنائي كبير" يتضمّن الطريقة GET، و3) المعالج الرئيسي الذي يتضمّن الطريقتَين GET وPOST. أجرِ هذه التغييرات لكي يبدو باقي تطبيقك كما هو موضّح أدناه.

بعد:

class UploadHandler(blobstore_handlers.BlobstoreUploadHandler):
    'Upload blob (POST) handler'
    def post(self):
        uploads = self.get_uploads()
        blob_id = uploads[0].key() if uploads else None
        store_visit(self.request.remote_addr, self.request.user_agent, blob_id)
        self.redirect('/', code=307)

class ViewBlobHandler(blobstore_handlers.BlobstoreDownloadHandler):
    'view uploaded blob (GET) handler'
    def get(self, blob_key):
        self.send_blob(blob_key) if blobstore.get(blob_key) else self.error(404)

class MainHandler(BaseHandler):
    'main application (GET/POST) handler'
    def get(self):
        self.render_response('index.html',
                upload_url=blobstore.create_upload_url('/upload'))

    def post(self):
        visits = fetch_visits(10)
        self.render_response('index.html', visits=visits)

app = webapp2.WSGIApplication([
    ('/', MainHandler),
    ('/upload', UploadHandler),
    ('/view/([^/]+)?', ViewBlobHandler),
], debug=True)

هناك عدّة طلبات رئيسية في الرمز الذي أضفناه للتو:

  • في MainHandler.get، هناك مكالمة إلى blobstore.create_upload_url. يؤدي هذا الاستدعاء إلى إنشاء عنوان URL الذي يتم إرسال النموذج POST إليه، ويتم استدعاء معالج التحميل لإرسال الملف إلى Blobstore.
  • في UploadHandler.post، هناك مكالمة إلى blobstore_handlers.BlobstoreUploadHandler.get_uploads. هذه هي الميزة السحرية التي تضع الملف في Blobstore وتعرض معرّفًا فريدًا ودائمًا لهذا الملف، وهو BlobKey.
  • في ViewBlobHandler.get، يؤدي طلب blobstore_handlers.BlobstoreDownloadHandler.send باستخدام BlobKey لملف إلى جلب الملف وإعادة توجيهه إلى متصفح المستخدم النهائي.

تمثّل هذه الاستدعاءات الجزء الأكبر من الوصول إلى الميزات المضافة إلى التطبيق. إليك تمثيلاً مصوّرًا لهذه المجموعة الثانية والأخيرة من التغييرات على main.py:

da2960525ac1b90d.png

تعديل نموذج HTML

تؤثّر بعض التحديثات على التطبيق الرئيسي في واجهة مستخدم التطبيق، لذا يجب إجراء تغييرات مقابلة في نموذج الويب، وتحديدًا تغييرَين:

  1. يجب توفير نموذج لتحميل الملفات يتضمّن 3 عناصر إدخال: ملف وزرّي إرسال لتحميل الملف وتخطّيه على التوالي.
  2. عدِّل ناتج الزيارات الأخيرة من خلال إضافة رابط "عرض" للزيارات التي تم تحميل ملف مطابق لها، أو "لا شيء" في الحالات الأخرى.

قبل:

<!doctype html>
<html>
<head>
<title>VisitMe Example</title>
<body>

<h1>VisitMe example</h1>
<h3>Last 10 visits</h3>
<ul>
{% for visit in visits %}
    <li>{{ visit.timestamp.ctime }} from {{ visit.visitor }}</li>
{% endfor %}
</ul>

</body>
</html>

نفِّذ التغييرات الواردة في القائمة أعلاه لتضمين النموذج المعدَّل:

بعد:

<!doctype html>
<html>
<head>
<title>VisitMe Example</title>
<body>

<h1>VisitMe example</h1>
{% if upload_url %}

<h3>Welcome... upload a file? (optional)</h3>
<form action="{{ upload_url }}" method="POST" enctype="multipart/form-data">
    <input type="file" name="file"><p></p>
    <input type="submit"> <input type="submit" value="Skip">
</form>

{% else %}

<h3>Last 10 visits</h3>
<ul>
{% for visit in visits %}
<li>{{ visit.timestamp.ctime() }}
    <i><code>
    {% if visit.file_blob %}
        (<a href="/view/{{ visit.file_blob }}" target="_blank">view</a>)
    {% else %}
        (none)
    {% endif %}
    </code></i>
    from {{ visit.visitor }}
</li>
{% endfor %}
</ul>

{% endif %}

</body>
</html>

توضّح هذه الصورة التعديلات المطلوبة على index.html:

8583e975f25aa9e7.png

التغيير الأخير هو أنّ Jinja2 يفضّل أن تكون قوالبه في مجلد templates، لذا أنشئ هذا المجلد وانقل index.html إلى داخله. بهذه الخطوة الأخيرة، تكون قد انتهيت من جميع التغييرات اللازمة لإضافة استخدام Blobstore إلى نموذج التطبيق Module 0.

(اختياري) "تحسين" Cloud Storage

تطوّر تخزين Blobstore في النهاية ليصبح Cloud Storage نفسه. وهذا يعني أنّ عمليات التحميل إلى Blobstore تظهر في Cloud Console، وتحديدًا في متصفّح Cloud Storage. والسؤال هو أين. الإجابة هي حزمة Cloud Storage التلقائية لتطبيق App Engine. اسمها هو اسم النطاق الكامل لتطبيق App Engine، أي PROJECT_ID.appspot.com. هذا مفيد جدًا لأنّ جميع أرقام تعريف المشاريع فريدة، أليس كذلك؟

تؤدي التعديلات التي تم إجراؤها على التطبيق النموذجي إلى إسقاط الملفات التي تم تحميلها في هذا الحزمة، ولكن يمكن للمطوّرين اختيار موقع أكثر تحديدًا. يمكن الوصول إلى الحزمة التلقائية آليًا من خلال google.appengine.api.app_identity.get_default_gcs_bucket_name()، ما يتطلّب عملية استيراد جديدة إذا أردت الوصول إلى هذه القيمة، مثلاً لاستخدامها كبادئة لتنظيم الملفات التي تم تحميلها. على سبيل المثال، الفرز حسب نوع الملف:

f61f7a23a1518705.png

لتنفيذ شيء من هذا القبيل للصور، على سبيل المثال، سيكون لديك رمز مثل هذا مع بعض الرموز التي تتحقّق من أنواع الملفات لاختيار اسم الحزمة المطلوب:

ROOT_BUCKET = app_identity.get_default_gcs_bucket_name()
IMAGE_BUCKET = '%s/%s' % (ROOT_BUCKET, 'images')

ستتحقّق أيضًا من صحة الصور التي تم تحميلها باستخدام أداة مثل وحدة imghdr في مكتبة Python Standard Library للتأكّد من نوع الصورة. أخيرًا، من المحتمل أن تحتاج إلى الحدّ من حجم عمليات التحميل في حال وجود مستخدمين مسيئين.

لنفترض أنّ كل ما سبق قد تم. كيف يمكننا تعديل تطبيقنا للسماح بتحديد مكان تخزين الملفات التي تم تحميلها؟ المهم هو تعديل طلب blobstore.create_upload_url في MainHandler.get لتحديد الموقع الجغرافي المطلوب في Cloud Storage للتحميل عن طريق إضافة المَعلمة gs_bucket_name على النحو التالي:

blobstore.create_upload_url('/upload', gs_bucket_name=IMAGE_BUCKET))

بما أنّ هذا التحديث اختياري إذا كنت تريد تحديد المكان الذي يجب أن يتم تحميل الملفات إليه، فهو ليس جزءًا من ملف main.py في المستودع. بدلاً من ذلك، يتوفّر بديل باسم main-gcs.py لمراجعتك في المستودع. بدلاً من استخدام "مجلد" حزمة منفصل، يخزّن الرمز البرمجي في main-gcs.py عمليات التحميل في حزمة "الجذر" (PROJECT_ID.appspot.com) تمامًا مثل main.py، ولكنّه يوفّر لك البنية الأساسية التي تحتاج إليها إذا أردت تحويل العيّنة إلى شيء آخر كما هو موضّح في هذا القسم. في ما يلي توضيح "للفروق" بين main.py وmain-gcs.py.

256e1ea68241a501.png

6. الملخّص/التنظيف

يختتم هذا القسم الدرس التطبيقي حول الترميز من خلال تفعيل التطبيق والتأكّد من عمله على النحو المطلوب وفي أي ناتج معروض. بعد التحقّق من التطبيق، اتّخِذ أي خطوات تنظيف وفكِّر في الخطوات التالية.

نشر التطبيق والتحقّق منه

أعِد نشر تطبيقك باستخدام gcloud app deploy، وتأكَّد من أنّ التطبيق يعمل على النحو المُعلن عنه، ويختلف في تجربة المستخدم (UX) عن تطبيق الوحدة 0. يتضمّن تطبيقك الآن شاشتَين مختلفتَين، الأولى هي طلب نموذج تحميل ملف الزيارة:

f5b5f9f19d8ae978.pngبعد ذلك، يمكن للمستخدمين النهائيين إما تحميل ملف والنقر على "إرسال" أو النقر على "تخطّي" لعدم تحميل أي شيء. في كلتا الحالتين، تكون النتيجة هي شاشة الزيارة الأحدث، والتي تمّت إضافة روابط "عرض" أو "لا شيء" إليها بين الطوابع الزمنية للزيارة ومعلومات الزائر:

f5ac6b98ee8a34cb.png

تهانينا على إكمال هذا الدرس التطبيقي حول الترميز الذي يضيف استخدام App Engine Blobstore إلى نموذج تطبيق الوحدة 0. من المفترض أن يتطابق الرمز البرمجي الآن مع ما هو موجود في المجلد FINISH (الوحدة 15). يتوفّر البديل main-gcs.py أيضًا في هذا المجلد.

تَنظيم

للجمهور العام

إذا انتهيت من استخدام التطبيق في الوقت الحالي، ننصحك بإيقاف تطبيق App Engine لتجنُّب تحمّل رسوم. ومع ذلك، إذا أردت إجراء المزيد من الاختبارات أو التجارب، تتضمّن منصة App Engine حصّة مجانية، وبالتالي لن يتم تحصيل رسوم منك طالما أنّك لا تتجاوز مستوى الاستخدام هذا. هذا السعر خاص بالحوسبة، ولكن قد تكون هناك رسوم أيضًا مقابل خدمات App Engine ذات الصلة، لذا يُرجى الاطّلاع على صفحة الأسعار للحصول على مزيد من المعلومات. إذا كان نقل البيانات هذا يتضمّن خدمات سحابية أخرى، يتم إصدار فواتير لها بشكل منفصل. في كلتا الحالتين، إذا كان ذلك منطبقًا، راجِع القسم "خاص بهذا الدرس التطبيقي حول الترميز" أدناه.

للتوضيح، يؤدي النشر على منصة حوسبة بدون خادم في Google Cloud، مثل App Engine، إلى تكبُّد تكاليف بسيطة للإنشاء والتخزين. تتضمّن Cloud Build حصة مجانية خاصة بها، وكذلك Cloud Storage. ويؤدي تخزين هذه الصورة إلى استهلاك جزء من هذه الحصة. ومع ذلك، قد تكون مقيمًا في منطقة لا تتوفّر فيها هذه الطبقة المجانية، لذا عليك الانتباه إلى استخدامك لمساحة التخزين لتقليل التكاليف المحتملة. تتضمّن "المجلدات" المحدّدة في Cloud Storage التي يجب مراجعتها ما يلي:

  • console.cloud.google.com/storage/browser/LOC.artifacts.PROJECT_ID.appspot.com/containers/images
  • console.cloud.google.com/storage/browser/staging.PROJECT_ID.appspot.com
  • تعتمد روابط مساحة التخزين أعلاه على PROJECT_ID و *LOC*، على سبيل المثال، "us" إذا كان تطبيقك مستضافًا في الولايات المتحدة.

من ناحية أخرى، إذا كنت لن تواصل استخدام هذا التطبيق أو غيره من الدروس التعليمية البرمجية المتعلقة بنقل البيانات وأردت حذف كل شيء تمامًا، عليك إيقاف مشروعك.

خاص بهذا الدرس التطبيقي حول الترميز

الخدمات المدرَجة أدناه خاصة بهذا الدرس التطبيقي حول الترميز. يُرجى الرجوع إلى مستندات كل منتج للحصول على مزيد من المعلومات:

الخطوات التالية

تتناول الوحدة 16 عملية النقل المنطقية التالية التي يجب أخذها في الاعتبار، وتوضّح للمطوّرين كيفية نقل البيانات من خدمة Blobstore في App Engine إلى استخدام مكتبة برامج Cloud Storage. تشمل مزايا الترقية إمكانية الوصول إلى المزيد من ميزات Cloud Storage، والتعرّف على مكتبة برامج تعمل مع التطبيقات خارج App Engine، سواء في Google Cloud أو السُحب الإلكترونية الأخرى أو حتى داخل الشركة. إذا كنت لا تحتاج إلى جميع الميزات المتاحة من Cloud Storage أو كنت قلقًا بشأن تأثيرها في التكلفة، يمكنك مواصلة استخدام App Engine Blobstore.

بالإضافة إلى الوحدة 16، هناك مجموعة كبيرة من عمليات النقل المحتملة الأخرى، مثل Cloud NDB وCloud Datastore أو Cloud Tasks أو Cloud Memorystore. تتوفّر أيضًا عمليات نقل بيانات بين المنتجات إلى Cloud Run وCloud Functions. يتضمّن مستودع نقل البيانات جميع عيّنات الرموز البرمجية، ويوفر روابط تؤدي إلى جميع الدورات التدريبية عبر الإنترنت والفيديوهات المتاحة، كما يقدّم إرشادات حول عمليات نقل البيانات التي يجب أخذها في الاعتبار وأي "ترتيب" ذي صلة لعمليات نقل البيانات.

7. مراجع إضافية

مشاكل/ملاحظات بشأن الدروس التطبيقية حول الترميز

إذا واجهت أي مشاكل في هذا الدرس العملي، يُرجى البحث عن مشكلتك أولاً قبل إرسالها. روابط للبحث عن مشاكل جديدة وإنشائها:

مراجع لنقل البيانات

يمكنك العثور على روابط لمجلدات المستودع الخاصة بالوحدة 0 (البداية) والوحدة 15 (النهاية) في الجدول أدناه. يمكن أيضًا الوصول إليها من مستودع جميع عمليات نقل Codelab في App Engine الذي يمكنك استنساخه أو تنزيل ملف ZIP منه.

Codelab

Python 2

Python 3

الوحدة رقم 0

code

لا ينطبق

الوحدة 15 (هذا الدرس التطبيقي حول الترميز)

code

لا ينطبق

مراجع متوفرة على الإنترنت

في ما يلي مراجع على الإنترنت قد تكون ذات صلة بهذا البرنامج التعليمي:

App Engine

Google Cloud

Python

الفيديوهات

الترخيص

يخضع هذا العمل لترخيص المشاع الإبداعي مع نسب العمل إلى مؤلفه 2.0 Generic License.