بارگذاری مجدد رسانه در پروتکل داده Google

اریک بیدلمن، تیم G Suite APIs
فوریه 2010

  1. معرفی
  2. پروتکل Resumable
    1. شروع یک درخواست بارگذاری مجدد
    2. در حال آپلود یک فایل
    3. از سرگیری آپلود
    4. لغو آپلود
    5. به روز رسانی یک منبع موجود
  3. نمونه های کتابخانه مشتری

معرفی

استانداردهای فعلی وب هیچ مکانیسم قابل اعتمادی برای تسهیل آپلود HTTP فایل های بزرگ ارائه نمی دهند. در نتیجه، آپلود فایل ها در گوگل و سایر سایت ها به طور سنتی به اندازه های متوسط ​​(مثلاً 100 مگابایت) محدود شده است. برای سرویس‌هایی مانند YouTube و APIهای فهرست اسناد Google که از آپلود فایل‌های بزرگ پشتیبانی می‌کنند، این یک مانع بزرگ است.

پروتکل Google Data Resumable مستقیماً با پشتیبانی از درخواست‌های POST/PUT HTTP قابل تجدید در HTTP/1.0 به مشکلات فوق‌الذکر می‌پردازد. این پروتکل بر اساس ResumableHttpRequestsProposal پیشنهاد شده توسط تیم Google Gears مدل شده است.

این سند نحوه ادغام ویژگی بارگذاری مجدد Google Data را در برنامه های خود توضیح می دهد. مثال‌های زیر از API داده‌های فهرست اسناد Google استفاده می‌کنند. توجه داشته باشید که APIهای Google اضافی که این پروتکل را پیاده‌سازی می‌کنند ممکن است نیازمندی‌ها/کدهای پاسخ و غیره کمی متفاوت باشند. لطفاً برای جزئیات بیشتر به اسناد سرویس مراجعه کنید.

پروتکل Resumable

شروع یک درخواست بارگذاری مجدد

برای شروع یک جلسه آپلود مجدد، یک درخواست HTTP POST را به پیوند ارسال مجدد ارسال کنید. این پیوند در سطح فید یافت می شود. پیوند Resumable-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 باشد و نباید حاوی محتوای واقعی فایل باشد. مثال زیر یک درخواست قابل ازسرگیری برای آپلود یک پی دی اف بزرگ ایجاد می کند و با استفاده از سربرگ 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 باید روی mimetype و اندازه فایلی که در نهایت آپلود می کنید تنظیم شوند. اگر طول محتوا در ایجاد جلسه آپلود ناشناخته باشد، هدر 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>

URI آپلود منحصر به فرد برای آپلود تکه های فایل استفاده خواهد شد.

توجه : درخواست POST اولیه ورودی جدیدی در فید ایجاد نمی کند. این تنها زمانی اتفاق می افتد که کل عملیات آپلود کامل شده باشد.

توجه : URI جلسه قابل ازسرگیری پس از یک هفته منقضی می شود.

در حال آپلود یک فایل

پروتکل قابل ازسرگیری اجازه می دهد، اما نیازی به آپلود محتوا در «تکه ها» ندارد، زیرا هیچ محدودیت ذاتی در HTTP در اندازه درخواست وجود ندارد. مشتری شما آزاد است که اندازه قطعه خود را انتخاب کند یا فقط فایل را به عنوان یک کل آپلود کند. این مثال از URI آپلود منحصر به فرد برای صدور یک 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 هر تکه از فایل تا زمانی که کل فایل آپلود شود، ادامه دهد. تا زمانی که آپلود کامل شود، سرور با 308 Resume Incomplete و محدوده بایتی که در هدر Range از آن اطلاع دارد، پاسخ می‌دهد. کلاینت ها باید از هدر Range برای تعیین محل شروع قطعه بعدی استفاده کنند. بنابراین، فرض نکنید که سرور تمام بایت های ارسال شده در درخواست PUT را دریافت کرده است.

توجه : سرور ممکن است یک URI آپلود منحصربفرد جدید در سرصفحه Location در طول یک قطعه صادر کند. مشتری شما باید Location به روز شده را بررسی کند و از آن URI برای ارسال تکه های باقی مانده به سرور استفاده کند.

هنگامی که آپلود کامل شد، پاسخ همان خواهد بود که اگر آپلود با استفاده از مکانیسم آپلود غیرقابل ادامه API انجام شده باشد. به این معنا که یک 201 Created به همراه <atom:entry> که توسط سرور ایجاد شده است، برگردانده می شود. PUT های بعدی به URI آپلود منحصربفرد، همان پاسخی را که پس از تکمیل آپلود برگردانده شد، برمی گرداند. پس از مدتی، پاسخ 410 Gone یا 404 Not Found خواهد بود.

از سرگیری آپلود

اگر درخواست شما قبل از دریافت پاسخ از سرور خاتمه یافته است یا اگر پاسخ HTTP 503 از سرور دریافت می‌کنید، می‌توانید با صدور یک درخواست PUT خالی در URI آپلود منحصربه‌فرد، وضعیت فعلی آپلود را جویا شوید.

کلاینت از سرور نظرسنجی می کند تا مشخص کند کدام بایت را دریافت کرده است:

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 به روز شده را بررسی کند و از آن URI برای ارسال تکه های باقی مانده به سرور استفاده کند.

در نهایت، کلاینت از جایی که سرور متوقف شده از سر گرفته می شود:

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

<bytes 43-99>

لغو آپلود

اگر می‌خواهید آپلود را لغو کنید و از هرگونه اقدام بیشتر در مورد آن جلوگیری کنید، یک درخواست DELETE در URI آپلود منحصر به فرد صادر کنید.

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 ' به پیوند ورودی ارسال کنید. اگر API از به‌روزرسانی محتوای منبع پشتیبانی کند، هر 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>

بازگشت به بالا

نمونه های کتابخانه مشتری

در زیر نمونه‌هایی از آپلود یک فایل فیلم در Google Docs (با استفاده از پروتکل بارگذاری مجدد) در کتابخانه‌های سرویس گیرنده Google Data آمده است. توجه داشته باشید که در حال حاضر همه کتابخانه‌ها از ویژگی resumable پشتیبانی نمی‌کنند.

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

برای نمونه های کامل و مرجع کد منبع، به منابع زیر مراجعه کنید:

بازگشت به بالا