Uploads von fortsetzbaren Medien im Google-Datenprotokoll

Eric Bidelman, G Suite APIs-Team
Februar 2010

  1. Einführung
  2. Fortsetzbares Protokoll
    1. Anfrage für einen fortsetzbaren Upload starten
    2. Datei hochladen
    3. Upload fortsetzen
    4. Upload abbrechen
    5. Vorhandene Ressource aktualisieren
  3. Clientbibliotheken – Beispiele

Einführung

Aktuelle Webstandards bieten keinen zuverlässigen Mechanismus, um den HTTP-Upload großer Dateien zu vereinfachen. Daher waren Dateiuploads bei Google und auf anderen Websites bisher auf mittelgroße Größen (z.B. 100 MB) beschränkt. Bei Diensten wie YouTube und den Google Documents List APIs, die große Dateiuploads unterstützen, stellt dies eine große Hürde dar.

Das fortsetzbare Protokoll von Google Data behebt direkt die oben genannten Probleme, indem es fortsetzbare POST/PUT-HTTP-Anfragen in HTTP/1.0 unterstützt. Das Protokoll wurde nach dem vom Google Gear-Team vorgeschlagenen ResumableHttpRequestsOffer modelliert.

In diesem Dokument wird beschrieben, wie Sie die Google-Funktion für fortsetzbare Uploads in Ihre Anwendungen einbinden. In den folgenden Beispielen wird die Google Documents List Data API verwendet. Beachten Sie, dass weitere Google APIs, die dieses Protokoll implementieren, möglicherweise unterschiedliche Anforderungen, Antwortcodes usw. haben. Weitere Informationen finden Sie in der Dokumentation des Dienstes.

Fortsetzbares Protokoll

Anfrage für einen fortsetzbaren Upload starten

Senden Sie zum Starten einer fortsetzbaren Uploadsitzung eine HTTP-POST-Anfrage an den Link für fortsetzbare Beiträge. Dieser Link befindet sich auf Feedebene. Der Link zur DocList API für fortsetzbare Beiträge sieht folgendermaßen aus:

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

Der Text Ihrer POST-Anfrage sollte leer sein oder einen Atom-XML-Eintrag enthalten und darf nicht den tatsächlichen Dateiinhalt enthalten. Im folgenden Beispiel wird eine fortsetzbare Anfrage zum Hochladen einer großen PDF-Datei erstellt und ein Titel für das zukünftige Dokument mit dem Header Slug hinzugefügt.

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

Die Header X-Upload-Content-Type und X-Upload-Content-Length sollten auf den MIME-Typ und die Größe der Datei festgelegt sein, die Sie schließlich hochladen möchten. Wenn die Inhaltslänge beim Erstellen der Uploadsitzung unbekannt ist, kann der Header X-Upload-Content-Length weggelassen werden.

Hier ist eine weitere Beispielanfrage, bei der stattdessen ein Wortdokument hochgeladen wird. Dieses Mal sind Atom-Metadaten enthalten und werden auf den endgültigen Dokumenteintrag angewendet.

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>

Die Antwort des Servers aus dem ersten POST ist ein eindeutiger Upload-URI im Location-Header und ein leerer Antworttext:

HTTP/1.1 200 OK
Location: <upload_uri>

Der eindeutige Upload-URI wird zum Hochladen der Dateiblöcke verwendet.

Hinweis: Durch die erste POST-Anfrage wird kein neuer Eintrag im Feed erstellt. Dies geschieht nur, wenn der gesamte Uploadvorgang abgeschlossen ist.

Hinweis: Ein URI einer fortsetzbaren Sitzung läuft nach einer Woche ab.

Datei hochladen

Das Fortsetzbare Protokoll lässt zu, dass Inhalte auch in Teilen hochgeladen werden, da es in HTTP keine Beschränkungen für die Anfragegrößen gibt. Der Kunde kann die Größe des Blocks auswählen oder die Datei als Ganzes hochladen. In diesem Beispiel wird der eindeutige Upload-URI verwendet, um einen fortsetzbaren PUT auszugeben. Im folgenden Beispiel werden die ersten 100.000 Byte der 1234.567 Byte großen PDF-Datei gesendet:

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

bytes 0-99999

Wenn die Größe der PDF-Datei unbekannt ist, wird in diesem Beispiel Content-Range: bytes 0-99999/* verwendet. Weitere Informationen zum Header Content-Range

Der Server antwortet mit dem aktuellen Bytebereich, der gespeichert wurde:

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

Ihr Client sollte so lange jeden Abschnitt der Datei PUT ausführen, bis die gesamte Datei hochgeladen wurde. Bis der Upload abgeschlossen ist, antwortet der Server mit dem HTTP-HTTP-Wert 308 Resume Incomplete und dem Bytebereich, den er kennt, im Header Range. Clients müssen den Header Range verwenden, um zu ermitteln, wo der nächste Teil beginnt. Gehen Sie daher nicht davon aus, dass der Server alle Byte erhalten hat, die ursprünglich in der PUT-Anfrage gesendet wurden.

Hinweis: Der Server gibt während eines Teils möglicherweise einen neuen eindeutigen Upload-URI im Location-Header aus. Ihr Client sollte nach einem aktualisierten Location suchen und diesen URI verwenden, um die verbleibenden Blöcke an den Server zu senden.

Wenn der Upload abgeschlossen ist, ist die Antwort die gleiche wie bei dem Upload mit dem nicht wiederaufnehmbaren Uploadmechanismus der API. Das heißt, zusammen mit der Datei <atom:entry> wird eine 201 Created zurückgegeben, die vom Server erstellt wurde. Die nachfolgenden PUTs an den eindeutigen Upload-URI geben dieselbe Antwort zurück wie der, der nach Abschluss des Uploads zurückgegeben wurde. Nach einem bestimmten Zeitraum lautet die Antwort 410 Gone oder 404 Not Found.

Upload fortsetzen

Wenn Ihre Anfrage vor dem Empfang einer Antwort vom Server beendet wird oder Sie eine HTTP-503-Antwort vom Server erhalten, können Sie den aktuellen Status des Uploads abfragen. Senden Sie dazu eine leere PUT-Anfrage an den eindeutigen Upload-URI.

Der Client fragt den Server ab, um festzustellen, welche Byte er empfangen hat:

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

Verwende * als content_length, wenn die Länge nicht bekannt ist.

Der Server antwortet mit dem aktuellen Bytebereich:

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

Hinweis: Wenn der Server für die Sitzung keine Commits durchgeführt hat, wird der Header Range weggelassen.

Hinweis: Der Server gibt während eines Teils möglicherweise einen neuen eindeutigen Upload-URI im Location-Header aus. Ihr Client sollte nach einem aktualisierten Location suchen und diesen URI verwenden, um die verbleibenden Blöcke an den Server zu senden.

Zuletzt wird der Client dort fortgesetzt, wo der Server aufgehört hat:

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

<bytes 43-99>

Upload abbrechen

Wenn Sie den Upload abbrechen und weitere damit verbundene Aktionen verhindern möchten, senden Sie eine DELETE-Anfrage an den eindeutigen Upload-URI.

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

Wenn der Vorgang erfolgreich war, antwortet der Server, dass die Sitzung abgebrochen wurde, und antwortet mit demselben Code für weitere PUT- oder Abfragestatusanfragen:

HTTP/1.1 499 Client Closed Request

Hinweis: Wenn ein Upload ohne Stornierung abgebrochen wird, läuft er automatisch eine Woche nach der Erstellung ab.

Vorhandene Ressource aktualisieren

Ähnlich wie beim Starten einer fortsetzbaren Uploadsitzung können Sie den Inhalt einer vorhandenen Datei mit dem Protokoll für fortsetzbare Uploads ersetzen. Zum Starten einer Anfrage für eine fortsetzbare Aktualisierung senden Sie eine HTTP-PUT an den Link des Eintrags mit rel='...#resumable-edit-media'. Jedes Medium entry enthält einen solchen Link, wenn die API die Aktualisierung des Inhalts der Ressource unterstützt.

Ein Dokumenteintrag in der DocList API enthält beispielsweise einen Link wie diesen:

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

Die erste Anfrage wäre also:

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

Wenn Sie die Metadaten und den Inhalt einer Ressource gleichzeitig aktualisieren möchten, fügen Sie Atom XML anstelle eines leeren Textkörpers ein. Weitere Informationen finden Sie im Beispiel im Abschnitt Fortsetzbare Uploadanfrage starten.

Wenn der Server mit dem eindeutigen Upload-URI antwortet, senden Sie eine PUT mit Ihrer Nutzlast. Sobald Sie den eindeutigen Upload-URI haben, können Sie den Inhalt der Datei genauso aktualisieren wie eine Datei hochladen.

In diesem Beispiel wird der Inhalt des vorhandenen Dokuments in einer Aufnahme aktualisiert:

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

<bytes 0-999>

Nach oben

Beispiele für die Clientbibliothek

Im Folgenden finden Sie Beispiele für das Hochladen einer Filmdatei in Google Docs (mit dem Protokoll für fortsetzbare Uploads) in den Google Data-Clientbibliotheken. Beachten Sie, dass nicht alle Bibliotheken die fortsetzbare Funktion unterstützen.

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

Vollständige Beispiele und Quellcodereferenzen finden Sie in den folgenden Ressourcen:

Nach oben