Google Veri Protokolü'nde devam ettirilebilir medya yüklemeleri

Eric Bidelman, G Suite API'leri ekibi
Şubat 2010

  1. Giriş
  2. The Resumable Protocol
    1. Devam ettirilebilir 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 ile yüklenmesini kolaylaştıracak güvenilir bir mekanizma sağlamaz. Bu nedenle, Google ve diğer sitelerde dosya yüklemeleri geleneksel olarak orta boyutlarla (ör. 100 MB) sınırlı olmuştur. YouTube ve Google Doküman Listesi API'leri gibi büyük dosya yüklemelerini destekleyen hizmetler için bu durum büyük bir engel teşkil eder.

Google Verileri'nin devam ettirilebilir protokolü, HTTP/1.0'da devam ettirilebilir POST/PUT HTTP isteklerini destekleyerek yukarıda belirtilen sorunları doğrudan ele alır. Protokol, Google Gears ekibinin önerdiği ResumableHttpRequestsProposal'a göre modellenmiştir.

Bu belgede, Google Verileri'nin devam ettirilebilir yükleme özelliğini uygulamalarınıza nasıl dahil edeceğiniz açıklanmaktadır. Aşağıdaki örneklerde Google Documents List Data API kullanılmaktadır. Bu protokolü uygulayan ek Google API'lerinin biraz farklı gereksinimleri/yanıt kodları/vb. 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 bir yükleme oturumu başlatmak için devam ettirilebilir yayın bağlantısına bir HTTP POST isteği gönderin. Bu bağlantı, feed düzeyinde bulunur. DocList API'nin devam ettirilebilir yayın bağlantısı şu şekilde 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 bir Atom XML girişi içermeli ve gerçek dosya içeriklerini içermemelidir. Aşağıdaki örnekte, büyük bir PDF'yi yüklemek için devam ettirilebilir bir istek oluşturuluyor ve Slug başlığı kullanılarak gelecekteki doküman için bir başlık ekleniyor.

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ı, sonunda yükleyeceğiniz dosyanın MIME türüne ve boyutuna ayarlanmalıdır. Yükleme oturumu oluşturulurken içerik uzunluğu bilinmiyorsa X-Upload-Content-Length üstbilgisi atlanabilir.

Aşağıda, bunun yerine bir Word belgesi yükleyen başka bir örnek istek verilmiştir. Bu kez Atom meta verileri eklenir ve nihai 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 üst bilgisinde 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ı bittiğinde gerçekleşir.

Not: Devam ettirilebilir oturum URI'sinin geçerlilik süresi bir haftadır.

Dosya yükleme

Devam ettirilebilir protokol, istek boyutlarıyla ilgili HTTP'de herhangi bir kısıtlama olmadığından içeriğin "parçalar" halinde yüklenmesine izin verir ancak bu zorunlu değildir. Müşteriniz, parça boyutunu seçebilir veya dosyayı tek parça 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, 1.234.567 baytlık 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/* değeri kullanılırdı. Content-Range başlığı hakkında daha fazla bilgiyi burada bulabilirsiniz.

Sunucu, depolanmış olan 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 dosyanın her bir parçasını PUT işlemeye devam etmelidir. Yükleme tamamlanana kadar sunucu, HTTP 308 Resume Incomplete ile yanıt verir ve Range üst bilgisinde bildiği bayt aralığını belirtir. İstemciler, bir sonraki parçanın 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, bir parça sırasında Location üstbilgisinde yeni bir benzersiz yükleme URI'si verebilir. İstemciniz, güncellenmiş bir Location olup olmadığını kontrol etmeli ve kalan parçaları sunucuya göndermek için bu URI'yi kullanmalıdır.

Yükleme tamamlandığında yanıt, yükleme API'nin devam ettirilemeyen yükleme mekanizması kullanılarak yapılmış gibi olur. Yani, sunucu tarafından oluşturulan 201 Created ile birlikte <atom:entry> değeri döndürülür. Benzersiz yükleme URI'sine yapılan sonraki PUT'ler, yükleme tamamlandığında döndürülen yanıtla aynı 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 sonlandırılırsa 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, hangi baytları aldığını belirlemek için sunucuyu yoklar:

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

Uzunluk bilinmiyorsa content_length olarak * kullanılmalıdır.

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 herhangi bir bayt işlememişse Range üstbilgisini atlar.

Not: Sunucu, bir parça sırasında Location üstbilgisinde yeni bir benzersiz yükleme URI'si verebilir. İstemciniz, güncellenmiş bir Location olup olmadığını kontrol etmeli ve kalan parçaları 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 etmek ve üzerinde başka işlem yapılmasını önlemek istiyorsanız benzersiz yükleme URI'si için DELETE isteği gönderin.

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

İşlem başarılı olursa sunucu, oturumun iptal edildiğini ve daha fazla PUT veya sorgu durumu istekleri için aynı kodla yanıt verir:

HTTP/1.1 499 Client Closed Request

Not: İptal edilmeden bırakılan yüklemelerin geçerlilik süresi, oluşturulmalarından bir hafta sonra dolar.

Mevcut bir kaynağı güncelleme

Devam ettirilebilir 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 rel='...#resumable-edit-media' ile girişin bağlantısına bir HTTP PUT gönderin. API, kaynağın içeriğinin güncellenmesini destekliyorsa her medya entry bu tür bir bağlantı içerir.

Örneğin, DocList API'deki bir doküman girişi şu bağlantıya 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"/>

Bu nedenle, ilk istek şu şekilde 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ş bir gövde yerine Atom XML'yi ekleyin. Devam ettirilebilir yükleme isteği başlatma bölümündeki örneğe bakın.

Sunucu, benzersiz yükleme URI'siyle yanıt verdiğinde yükünüzle birlikte bir PUT gönderin. Benzersiz yükleme URI'sini aldıktan sonra, dosyanın içeriğini güncelleme işlemi dosya yükleme ile aynıdır.

Bu örnek, mevcut belgenin içeriğini tek seferde günceller:

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 Data istemci kitaplıklarında Google Dokümanlar'a film dosyası yükleme (devam ettirilebilir yükleme protokolü kullanılarak) örnekleri verilmiştir. Şu anda kitaplıkların tümünde devam ettirme özelliğinin desteklenmediğ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

Tam örnekler ve kaynak kodu referansı için aşağıdaki kaynaklara bakın:

Başa dön