عمليات تحميل الوسائط القابلة للاستئناف في بروتوكول بيانات Google

إريك بيدلمان، فريق واجهات برمجة التطبيقات في G Suite
فبراير 2010

  1. مقدّمة
  2. بروتوكول قابل للاستئناف
    1. بدء طلب تحميل يمكن استئنافه
    2. تحميل ملف
    3. استئناف عملية تحميل
    4. إلغاء عملية تحميل
    5. تعديل مرجع حالي
  3. أمثلة على مكتبات العملاء

مقدمة

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

يعالج بروتوكول Google Data القابل للاستئناف المشاكل المذكورة أعلاه مباشرةً من خلال إتاحة طلبات HTTP POST/PUT القابلة للاستئناف في HTTP/1.0. تم تصميم البروتوكول وفقًا ResumableHttpRequestsProposal الذي اقترحه فريق Google Gears.

يوضّح هذا المستند كيفية دمج ميزة "التحميل القابل للاستئناف" في Google Data في تطبيقاتك. تستخدِم الأمثلة أدناه Google Documents List Data API. يُرجى العِلم أنّ واجهات برمجة التطبيقات الإضافية من Google التي تنفّذ هذا البروتوكول قد تتضمّن متطلبات أو رموز استجابة أو غير ذلك مختلفة قليلاً. يُرجى الرجوع إلى مستندات الخدمة لمعرفة التفاصيل.

بروتوكول الاستئناف

بدء طلب تحميل قابل للاستئناف

لبدء جلسة تحميل قابلة للاستئناف، أرسِل طلب HTTP POST إلى رابط resumable-post. يمكن العثور على هذا الرابط على مستوى الخلاصة. يبدو رابط post القابل للاستئناف في DocList API على النحو التالي:

<link rel="http://schemas.google.com/g/2005#resumable-create-media" type="application/atom+xml"
    href="https://docs.google.com/feeds/upload/create-session/default/private/full"/>

يجب أن يكون نص طلب POST فارغًا أو يحتوي على إدخال Atom XML، ويجب ألا يتضمّن محتوى الملف الفعلي. ينشئ المثال أدناه طلبًا قابلاً للاستئناف لتحميل ملف PDF كبير، ويتضمّن عنوانًا للمستند المستقبلي باستخدام العنوان Slug.

POST /feeds/upload/create-session/default/private/full HTTP/1.1
Host: docs.google.com
GData-Version: version_number
Authorization: authorization
Content-Length: 0
Slug: MyTitle
X-Upload-Content-Type: content_type
X-Upload-Content-Length: content_length

empty body

يجب ضبط العنوانَين X-Upload-Content-Type وX-Upload-Content-Length على نوع MIME وحجم الملف الذي سيتم تحميله في النهاية. إذا كان طول المحتوى غير معروف عند إنشاء جلسة التحميل، يمكن حذف العنوان X-Upload-Content-Length.

في ما يلي مثال آخر على طلب يحمّل مستند Word بدلاً من ذلك. في هذه المرة، سيتم تضمين بيانات Atom الوصفية وسيتم تطبيقها على إدخال المستند النهائي.

POST /feeds/upload/create-session/default/private/full?convert=false HTTP/1.1
Host: docs.google.com
GData-Version: version_number
Authorization: authorization
Content-Length: atom_metadata_content_length
Content-Type: application/atom+xml
X-Upload-Content-Type: application/msword
X-Upload-Content-Length: 7654321

<?xml version='1.0' encoding='UTF-8'?>
<entry xmlns="http://www.w3.org/2005/Atom" xmlns:docs="http://schemas.google.com/docs/2007">
  <category scheme="http://schemas.google.com/g/2005#kind"
      term="http://schemas.google.com/docs/2007#document"/>
  <title>MyTitle</title>
  <docs:writersCanInvite value="false"/>
</entry>

تكون استجابة الخادم من POST الأوّلي عبارة عن معرّف موارد منتظم (URI) فريد للتحميل في العنوان Location ونص استجابة فارغ:

HTTP/1.1 200 OK
Location: <upload_uri>

سيتم استخدام معرّف الموارد المنتظم الفريد للتحميل لتحميل أجزاء الملف.

ملاحظة: لا يؤدي طلب POST الأوّلي إلى إنشاء إدخال جديد في الخلاصة. لا يحدث ذلك إلا عند اكتمال عملية التحميل بأكملها.

ملاحظة: تنتهي صلاحية معرّف الموارد المنتظم (URI) الخاص بالجلسة القابلة للاستئناف بعد أسبوع واحد.

تحميل ملف

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

PUT upload_uri HTTP/1.1
Host: docs.google.com
Content-Length: 100000
Content-Range: bytes 0-99999/1234567

bytes 0-99999

إذا كان حجم ملف PDF غير معروف، سيستخدم هذا المثال Content-Range: bytes 0-99999/*. يمكنك الاطّلاع على مزيد من المعلومات حول العنوان Content-Range هنا.

يستجيب الخادم بنطاق البايت الحالي الذي تم تخزينه:

HTTP/1.1 308 Resume Incomplete
Content-Length: 0
Range: bytes=0-99999

على العميل مواصلة PUT كل جزء من الملف إلى أن يتم تحميل الملف بأكمله. إلى أن يكتمل التحميل، سيردّ الخادم برمز HTTP 308 Resume Incomplete ونطاق البايتات الذي يعرفه في العنوان Range. يجب أن يستخدم العملاء العنوان Range لتحديد مكان بدء الجزء التالي. لذلك، لا تفترض أنّ الخادم تلقّى جميع وحدات البايت التي تم إرسالها في الأصل في طلب PUT.

ملاحظة: قد يصدر الخادم عنوان URI جديدًا وفريدًا للتحميل في العنوان Location أثناء تحميل جزء. على العميل التحقّق من وجود Location معدَّل واستخدام معرّف الموارد المنتظم هذا لإرسال الأجزاء المتبقية إلى الخادم.

عند اكتمال عملية التحميل، سيكون الردّ مماثلاً للردّ الذي يتم تلقّيه عند إجراء عملية التحميل باستخدام أسلوب التحميل غير القابل للاستئناف في واجهة برمجة التطبيقات. وهذا يعني أنّه سيتم عرض 201 Created مع <atom:entry>، كما أنشأه الخادم. ستعرض طلبات PUT اللاحقة إلى معرّف الموارد الموحّد الفريد للتحميل الاستجابة نفسها التي تم عرضها عند اكتمال عملية التحميل. بعد فترة من الوقت، ستكون الاستجابة 410 Gone أو 404 Not Found.

استئناف عملية تحميل

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

يطلب العميل من الخادم بيانات لتحديد عدد البايتات التي تلقّاها:

PUT upload_uri HTTP/1.1
Host: docs.google.com
Content-Length: 0
Content-Range: bytes */content_length

استخدِم * كـ content_length إذا كان الطول غير معروف.

يستجيب الخادم بنطاق البايت الحالي:

HTTP/1.1 308 Resume Incomplete
Content-Length: 0
Range: bytes=0-42

ملاحظة: إذا لم يلتزم الخادم بأي وحدات بايت للجلسة، سيتم حذف العنوان Range.

ملاحظة: قد يصدر الخادم عنوان URI جديدًا وفريدًا للتحميل في العنوان Location أثناء تحميل جزء. على العميل التحقّق من وجود Location معدَّل واستخدام معرّف الموارد المنتظم هذا لإرسال الأجزاء المتبقية إلى الخادم.

أخيرًا، يستأنف العميل من حيث توقّف الخادم:

PUT upload_uri HTTP/1.1
Host: docs.google.com
Content-Length: 57
Content-Range: 43-99/100

<bytes 43-99>

إلغاء عملية تحميل

إذا أردت إلغاء عملية التحميل ومنع اتّخاذ أي إجراء آخر بشأنها، أرسِل طلب DELETE إلى معرّف الموارد المنتظم الفريد الخاص بعملية التحميل.

DELETE upload_uri HTTP/1.1
Host: docs.google.com
Content-Length: 0

في حال نجاح العملية، يستجيب الخادم بأنّه تم إلغاء الجلسة، ويستجيب بالرمز نفسه لطلبات PUT أو حالة الطلب الإضافية:

HTTP/1.1 499 Client Closed Request

ملاحظة: إذا تم إيقاف عملية تحميل بدون إلغائها، ستنتهي صلاحيتها تلقائيًا بعد أسبوع من إنشائها.

تعديل مرجع حالي

على غرار بدء جلسة تحميل قابلة للاستئناف، يمكنك استخدام بروتوكول التحميل القابل للاستئناف لاستبدال محتوى ملف حالي. لبدء طلب تعديل قابل للاستئناف، أرسِل طلب HTTP PUT إلى رابط الإدخال مع rel='...#resumable-edit-media'. سيحتوي كل entry وسائط على رابط من هذا النوع إذا كان واجهة برمجة التطبيقات تتيح تعديل محتوى المورد.

على سبيل المثال، سيتضمّن إدخال مستند في DocList API رابطًا مشابهًا لما يلي:

<link rel="http://schemas.google.com/g/2005#resumable-edit-media" type="application/atom+xml"
      href="https://docs.google.com/feeds/upload/create-session/default/private/full/document%3A12345"/>

وبالتالي، سيكون الطلب الأوّلي على النحو التالي:

PUT /feeds/upload/create-session/default/private/full/document%3A12345 HTTP/1.1
Host: docs.google.com
GData-Version: version_number
Authorization: authorization
If-Match: ETag | *
Content-Length: 0
X-Upload-Content-Length: content_length
X-Upload-Content-Type: content_type

empty body

لتعديل البيانات الوصفية والمحتوى الخاصَّين بمورد في الوقت نفسه، أدرِج Atom XML بدلاً من نص فارغ. يمكنك الاطّلاع على المثال في قسم بدء طلب تحميل قابل للاستئناف.

عندما يستجيب الخادم بمعرّف الموارد المنتظم (URI) الفريد للتحميل، أرسِل PUT مع الحمولة. بعد الحصول على معرّف الموارد المنتظم (URI) الفريد الخاص بالتحميل، تكون عملية تعديل محتوى الملف مماثلة لعملية تحميل ملف.

سيعدّل هذا المثال تحديدًا محتوى المستند الحالي دفعة واحدة:

PUT upload_uri HTTP/1.1
Host: docs.google.com
Content-Length: 1000
Content-Range: 0-999/1000

<bytes 0-999>

الرجوع إلى الأعلى

أمثلة على مكتبات العملاء

في ما يلي نماذج لتحميل ملف فيلم إلى &quot;مستندات Google&quot; (باستخدام بروتوكول التحميل القابل للاستئناف) في مكتبات برامج Google Data. يُرجى العلم أنّ بعض المكتبات لا تتيح حاليًا ميزة استئناف التنزيل.

int MAX_CONCURRENT_UPLOADS = 10;
int PROGRESS_UPDATE_INTERVAL = 1000;
int DEFAULT_CHUNK_SIZE = 10485760;


DocsService client = new DocsService("yourCompany-yourAppName-v1");
client.setUserCredentials("user@gmail.com", "pa$$word");

// Create a listener
FileUploadProgressListener listener = new FileUploadProgressListener(); // See the sample for details on this class.

// Pool for handling concurrent upload tasks
ExecutorService executor = Executors.newFixedThreadPool(MAX_CONCURRENT_UPLOADS);

// Create {@link ResumableGDataFileUploader} for each file to upload
List uploaders = Lists.newArrayList();

File file = new File("test.mpg");
String contentType = DocumentListEntry.MediaType.fromFileName(file.getName()).getMimeType();
MediaFileSource mediaFile = new MediaFileSource(file, contentType);
URL createUploadUrl = new URL("https://docs.google.com/feeds/upload/create-session/default/private/full");
ResumableGDataFileUploader uploader = new ResumableGDataFileUploader(createUploadUrl, mediaFile, client, DEFAULT_CHUNK_SIZE,
                                                                     executor, listener, PROGRESS_UPDATE_INTERVAL);
uploaders.add(uploader);

listener.listenTo(uploaders); // attach the listener to list of uploaders

// Start the upload(s)
for (ResumableGDataFileUploader uploader : uploaders) {
  uploader.start();
}

// wait for uploads to complete
while(!listener.isDone()) {
  try {
    Thread.sleep(100);
  } catch (InterruptedException ie) {
    listener.printResults();
    throw ie; // rethrow
  }
// Chunk size in MB
int CHUNK_SIZE = 1;

ClientLoginAuthenticator cla = new ClientLoginAuthenticator(
    "yourCompany-yourAppName-v1", ServiceNames.Documents, "user@gmail.com", "pa$$word");

// Set up resumable uploader and notifications
ResumableUploader ru = new ResumableUploader(CHUNK_SIZE);
ru.AsyncOperationCompleted += new AsyncOperationCompletedEventHandler(this.OnDone);
ru.AsyncOperationProgress += new AsyncOperationProgressEventHandler(this.OnProgress);

// Set metadata for our upload.
Document entry = new Document()
entry.Title = "My Video";
entry.MediaSource = new MediaFileSource("c:\\test.mpg", "video/mpeg");

// Add the upload uri to document entry.
Uri createUploadUrl = new Uri("https://docs.google.com/feeds/upload/create-session/default/private/full");
AtomLink link = new AtomLink(createUploadUrl.AbsoluteUri);
link.Rel = ResumableUploader.CreateMediaRelation;
entry.DocumentEntry.Links.Add(link);

ru.InsertAsync(cla, entry.DocumentEntry, userObject);
- (void)uploadAFile {
  NSString *filePath = @"~/test.mpg";
  NSString *fileName = [filePath lastPathComponent];

  // get the file's data
  NSData *data = [NSData dataWithContentsOfMappedFile:filePath];

  // create an entry to upload
  GDataEntryDocBase *newEntry = [GDataEntryStandardDoc documentEntry];
  [newEntry setTitleWithString:fileName];

  [newEntry setUploadData:data];
  [newEntry setUploadMIMEType:@"video/mpeg"];
  [newEntry setUploadSlug:fileName];

  // to upload, we need the entry, our service object, the upload URL,
  // and the callback for when upload has finished
  GDataServiceGoogleDocs *service = [self docsService];
  NSURL *uploadURL = [GDataServiceGoogleDocs docsUploadURL];
  SEL finishedSel = @selector(uploadTicket:finishedWithEntry:error:);

  // now start the upload
  GDataServiceTicket *ticket = [service fetchEntryByInsertingEntry:newEntry
                                                        forFeedURL:uploadURL
                                                          delegate:self
                                                 didFinishSelector:finishedSel];

  // progress monitoring is done by specifying a callback, like this
  SEL progressSel = @selector(ticket:hasDeliveredByteCount:ofTotalByteCount:);
  [ticket setUploadProgressSelector:progressSel];
}

// callback for when uploading has finished
- (void)uploadTicket:(GDataServiceTicket *)ticket
   finishedWithEntry:(GDataEntryDocBase *)entry
               error:(NSError *)error {
  if (error == nil) {
    // upload succeeded
  }
}

- (void)pauseOrResumeUploadForTicket:(GDataServiceTicket *)ticket {
  if ([ticket isUploadPaused]) {
    [ticket resumeUpload];
  } else {
    [ticket pauseUpload];
  }
}
import os.path
import atom.data
import gdata.client
import gdata.docs.client
import gdata.docs.data

CHUNK_SIZE = 10485760

client = gdata.docs.client.DocsClient(source='yourCompany-yourAppName-v1')
client.ClientLogin('user@gmail.com', 'pa$$word', client.source);

f = open('test.mpg')
file_size = os.path.getsize(f.name)

uploader = gdata.client.ResumableUploader(
    client, f, 'video/mpeg', file_size, chunk_size=CHUNK_SIZE, desired_class=gdata.docs.data.DocsEntry)

# Set metadata for our upload.
entry = gdata.docs.data.DocsEntry(title=atom.data.Title(text='My Video'))
new_entry = uploader.UploadFile('/feeds/upload/create-session/default/private/full', entry=entry)
print 'Document uploaded: ' + new_entry.title.text
print 'Quota used: %s' % new_entry.quota_bytes_used.text

للاطّلاع على أمثلة كاملة ومرجع الرمز المصدر، يُرجى الرجوع إلى المراجع التالية:

الرجوع إلى الأعلى