Google Veri Protokolü'nde Devam Ettirilebilir Medya Yüklemeleri

Eric Bidelman, G Suite API'ler Ekibi
Şubat 2010

  1. Giriş
  2. Devam Ettirilebilir Protokol
    1. Devam ettirilebilir bir yükleme isteği başlatma
    2. Dosya yükleme
    3. Yüklemeye devam etme
    4. Yüklemeyi iptal etme
    5. Mevcut bir kaynağı güncelleme
  3. İstemci kitaplığı örnekleri

Giriş

Mevcut web standartları, büyük dosyaların HTTP yüklemesini kolaylaştırmak için güvenilir bir mekanizma sağlamamaktadır. Bu nedenle, Google'daki ve diğer sitelerdeki dosya yüklemeleri geleneksel olarak orta boyutla (ör. 100 MB) sınırlandırılmıştır. Bu, YouTube ve Google Dokümanlar Listesi API'leri gibi büyük dosya yüklemelerini destekleyen hizmetler için büyük bir engel teşkil eder.

Google Verileri devam ettirilebilir protokolü, devam ettirilebilir POST/PUT HTTP isteklerini HTTP/1.0'da destekleyerek yukarıda bahsedilen sorunları doğrudan ele alır. Protokol, Google Gear ekibi tarafından önerilen ResumableHttpRequestsBid parametresinden sonra modellenmiştir.

Bu dokümanda, Google Verileri'nin devam ettirilebilir yükleme özelliğinin uygulamalarınıza nasıl dahil edileceği açıklanmaktadır. Aşağıdaki örneklerde Google Dokümanlar Listesi Veri API'si kullanılmaktadır. Bu protokolü uygulayan ek Google API'lerinin, gereksinimleri/yanıt kodları/vb. arasında küçük farklar olabileceğini unutmayın. Ayrıntılar için lütfen hizmetin dokümanlarına bakın.

Devam Ettirilebilir Protokol

Devam ettirilebilir yükleme isteği başlatma

Devam ettirilebilir yükleme oturumu başlatmak için devam ettirilebilir gönderi bağlantısına bir HTTP POST isteği gönderin. Bu bağlantı feed düzeyinde bulunur. DocList API'nın devam ettirilebilir yayın bağlantısı şöyle görünür:

<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 isteğinizin gövdesi boş olmalı veya Atom XML girişi içermeli ve gerçek dosya içeriğini içermemelidir. Aşağıdaki örnekte büyük bir PDF yüklemek için devam ettirilebilir bir istek oluşturulmuştur ve Slug başlığını kullanarak gelecekteki belge için bir başlık ekleyebilirsiniz.

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 ve X-Upload-Content-Length başlıkları, daha sonra yükleyeceğiniz dosyanın mime türü ve boyutuna ayarlanmalıdır. Yükleme oturumu oluşturulduğunda içerik uzunluğu bilinmiyorsa X-Upload-Content-Length başlığı atlanabilir.

Bunun yerine bir kelime dokümanı yükleyen başka bir örnek isteği burada görebilirsiniz. Bu kez Atom meta verileri dahil edilir ve son doküman girişine uygulanır.

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>

Sunucunun ilk POST yanıtı, Location başlığında benzersiz bir yükleme URI'si ve boş bir yanıt gövdesidir:

HTTP/1.1 200 OK
Location: <upload_uri>

Dosya parçalarını yüklemek için benzersiz yükleme URI'si kullanılır.

Not: İlk POST isteği, feed'de yeni bir giriş oluşturmaz. Bu durum, yalnızca yükleme işleminin tamamı tamamlandığında gerçekleşir.

Not: Devam ettirilebilen oturum URI'sinin süresi bir hafta sonra dolar.

Dosya yükleme

İstek boyutlarıyla ilgili HTTP'de yerleşik herhangi bir kısıtlama olmadığından, devam ettirilebilir protokol içeriğin "parçalar"a yüklenmesine izin verir ancak zorunlu değildir. İstemciniz, parça boyutunu seçebilir veya dosyayı bir bütün olarak yükleyebilir. Bu örnekte, devam ettirilebilir bir PUT yayınlamak için benzersiz yükleme URI'si kullanılmaktadır. Aşağıdaki örnek 1234567 bayt PDF dosyasının ilk 100.000 baytını gönderir:

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

bytes 0-99999

PDF dosyasının boyutu bilinmiyorsa bu örnekte Content-Range: bytes 0-99999/* kullanılır. Content-Range başlığı hakkında daha fazla bilgiyi burada bulabilirsiniz.

Sunucu, depolanan mevcut bayt aralığıyla yanıt verir:

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

İstemciniz, dosyanın tamamı yüklenene kadar her dosya parçasına PUT işlemi yapmaya devam etmelidir. Yükleme işlemi tamamlanana kadar sunucu, HTTP 308 Resume Incomplete ve bildiği bayt aralığını Range başlığında bildirir. Müşteriler, bir sonraki parçaya nereden başlayacağını belirlemek için Range üstbilgisini kullanmalıdır. Bu nedenle, sunucunun PUT isteğinde başlangıçta gönderilen tüm baytları aldığını varsaymayın.

Not: Sunucu, parça sırasında Location başlığında yeni bir benzersiz yükleme URI'si yayınlayabilir. İstemciniz, güncellenmiş bir Location olup olmadığını kontrol etmeli ve kalan URI'leri sunucuya göndermek için bu URI'yi kullanmalıdır.

Yükleme tamamlandığında yanıt, API'nin devam ettirilemeyen yükleme mekanizması kullanılarak yapılmış gibi yanıt verilir. Yani, 201 Created tarafından sunucu tarafından oluşturulan <atom:entry> ile birlikte döndürülür. Benzersiz yükleme URI'sine yapılan sonraki PUT'ler, yükleme tamamlandığında döndürülen yanıtı döndürür. Bir süre sonra yanıt 410 Gone veya 404 Not Found olur.

Yüklemeyi devam ettirme

İsteğiniz sunucudan yanıt almadan önce feshedilirse veya sunucudan bir HTTP 503 yanıtı alırsanız benzersiz yükleme URI'sinde boş bir PUT isteği göndererek yüklemenin mevcut durumunu sorgulayabilirsiniz.

İstemci, aldığı baytları belirlemek için sunucuyu sorgular:

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

Uzunluk bilinmiyorsa content_length olarak * kullanın.

Sunucu, geçerli bayt aralığıyla yanıt verir:

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

Not: Sunucu, oturum için bayt kullanmamışsa Range başlığını çıkarır.

Not: Sunucu, parça sırasında Location başlığında yeni bir benzersiz yükleme URI'si yayınlayabilir. İstemciniz, güncellenmiş bir Location olup olmadığını kontrol etmeli ve kalan URI'leri sunucuya göndermek için bu URI'yi kullanmalıdır.

Son olarak istemci, sunucunun kaldığı yerden devam eder:

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

<bytes 43-99>

Yüklemeyi iptal etme

Yüklemeyi iptal edip başka bir işlem yapılmasını önlemek istiyorsanız benzersiz yükleme URI'sinde DELETE isteği gönderin.

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

Başarılı olursa sunucu, oturumun iptal edildiğine ve daha sonra PUT ya da sorgu durumu istekleri için aynı kodla yanıt verir:

HTTP/1.1 499 Client Closed Request

Not: Yükleme işlemi iptal edilmeden reddedilirse doğal olarak videonun süresi sona erdikten bir hafta sonra sona erer.

Mevcut bir kaynağı güncelleme

Devam ettirilebilir bir yükleme oturumu başlatmaya benzer şekilde, mevcut bir dosyanın içeriğini değiştirmek için devam ettirilebilir yükleme protokolünü kullanabilirsiniz. Devam ettirilebilir bir güncelleme isteği başlatmak için girişin bağlantısında rel='...#resumable-edit-media' ile bir HTTP PUT gönderin. API, kaynağın içeriğini güncellemeyi destekliyorsa her medya entry bunu içerir.

Örneğin, DocList API'deki bir belge girişi aşağıdakine benzer bir bağlantı içerir:

<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"/>

İlk istek şöyle olur:

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

Bir kaynağın meta verilerini ve içeriğini aynı anda güncellemek için boş gövde yerine Atom XML ekleyin. Devam ettirilebilir bir yükleme isteği başlatma bölümündeki örneğe bakın.

Sunucu, benzersiz yükleme URI'si ile yanıt verdiğinde, yükünizle birlikte bir PUT gönderin. Benzersiz yükleme URI'niz olduğunda, dosyanın içeriğini güncelleme süreci dosya yükleme ile aynıdır.

Bu örnek, mevcut dokümanın içeriğini tek bir çekimde güncelleyecektir:

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

<bytes 0-999>

Başa dön

İstemci kitaplığı örnekleri

Aşağıda, Google Veri istemci kitaplıklarındaki Google'a film yükleme örneği (devam ettirilebilir yükleme protokolü kullanılarak) verilmiştir. Şu anda tüm kitaplıkların devam ettirilebilir özelliğini desteklemediğini unutmayın.

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

Örneklerin tamamı ve kaynak kodu referansı için aşağıdaki kaynaklara bakın:

Başa dön