Eric Bidelman, G Suite APIs-Team
Februar 2010
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 PUT
s 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>
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 Listuploaders = 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:
- Beispielanwendung und Quelle der Java-Bibliothek
- Objective-C-Bibliothek Beispiel-App
- .NET-Bibliothek Quelle