Eric Bidelman, tim G Suite API
Februari 2010
Pengantar
Standar web saat ini tidak menyediakan mekanisme yang andal untuk memfasilitasi upload file besar melalui HTTP. Akibatnya, upload file di Google dan situs lain biasanya dibatasi hingga ukuran sedang (misalnya, 100 MB). Untuk layanan seperti YouTube dan Google Documents List API yang mendukung upload file besar, hal ini menjadi kendala besar.
Protokol yang dapat dilanjutkan Google Data secara langsung mengatasi masalah yang disebutkan di atas dengan mendukung permintaan HTTP POST/PUT yang dapat dilanjutkan di HTTP/1.0. Protokol ini dibuat berdasarkan ResumableHttpRequestsProposal yang disarankan oleh tim Google Gears.
Dokumen ini menjelaskan cara menggabungkan fitur upload yang dapat dilanjutkan Google Data ke dalam aplikasi Anda. Contoh di bawah menggunakan Google Documents List Data API. Perhatikan bahwa API Google tambahan yang menerapkan protokol ini mungkin memiliki persyaratan/kode respons/dll. yang sedikit berbeda. Lihat dokumentasi layanan untuk mengetahui detailnya.
Protokol yang Dapat Dilanjutkan
Memulai permintaan upload yang dapat dilanjutkan
Untuk memulai sesi upload yang dapat dilanjutkan, kirim permintaan HTTP POST ke link resumable-post. Link ini ditemukan di tingkat feed.
Link resumable-post DocList API terlihat seperti:
<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"/>
Isi permintaan POST Anda harus kosong atau berisi entri XML Atom dan tidak boleh menyertakan konten file yang sebenarnya.
Contoh di bawah ini membuat permintaan yang dapat dilanjutkan untuk mengupload PDF berukuran besar, dan menyertakan judul untuk dokumen mendatang menggunakan header 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
Header X-Upload-Content-Type dan X-Upload-Content-Length harus disetel ke
mimetype dan ukuran file yang akan Anda upload. Jika panjang konten tidak diketahui saat
sesi upload dibuat, header X-Upload-Content-Length dapat dihilangkan.
Berikut adalah contoh permintaan lain yang mengupload dokumen Word. Kali ini, metadata Atom disertakan dan akan diterapkan ke entri dokumen akhir.
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>
Respons server dari POST awal adalah URI upload unik di header Location
dan isi respons kosong:
HTTP/1.1 200 OK
Location: <upload_uri>
URI upload unik akan digunakan untuk mengupload potongan file.
Catatan: Permintaan POST awal tidak membuat entri baru di feed.
Peristiwa ini hanya terjadi saat seluruh operasi upload telah selesai.
Catatan: Masa berlaku URI sesi yang dapat dilanjutkan akan berakhir setelah satu minggu.
Mengupload file
Protokol yang dapat dilanjutkan memungkinkan, tetapi tidak mewajibkan, konten diupload dalam 'potongan', karena tidak ada batasan bawaan dalam HTTP pada ukuran permintaan. Klien Anda bebas memilih ukuran chunk atau hanya mengupload file secara keseluruhan.
Contoh ini menggunakan URI upload unik untuk mengeluarkan PUT yang dapat dilanjutkan. Contoh berikut mengirimkan 100000 byte pertama file PDF 1234567 byte:
PUT upload_uri HTTP/1.1 Host: docs.google.com Content-Length: 100000 Content-Range: bytes 0-99999/1234567 bytes 0-99999
Jika ukuran file PDF tidak diketahui, contoh ini akan menggunakan Content-Range: bytes
0-99999/*. Baca informasi selengkapnya tentang header Content-Range
di sini.
Server merespons dengan rentang byte saat ini yang telah disimpan:
HTTP/1.1 308 Resume Incomplete Content-Length: 0 Range: bytes=0-99999
Klien Anda harus terus melakukan PUT pada setiap bagian file hingga seluruh file diupload.
Hingga upload selesai, server akan merespons dengan HTTP 308 Resume Incomplete dan rentang byte yang diketahuinya
di header Range. Klien harus menggunakan header Range untuk menentukan tempat memulai potongan berikutnya.
Oleh karena itu, jangan berasumsi bahwa server menerima semua byte yang awalnya dikirim dalam permintaan PUT.
Catatan: Server dapat mengeluarkan URI upload unik baru di header Location selama chunk. Klien Anda harus memeriksa Location yang telah diupdate dan menggunakan URI tersebut untuk mengirimkan potongan yang tersisa ke server.
Setelah upload selesai, respons akan sama seperti jika upload dilakukan menggunakan
mekanisme upload non-dapat dilanjutkan API. Artinya, 201 Created akan ditampilkan bersama dengan <atom:entry>,
seperti yang dibuat oleh server. PUT berikutnya ke URI upload unik akan menampilkan respons yang sama dengan yang ditampilkan saat upload selesai.
Setelah jangka waktu tertentu, responsnya akan menjadi 410 Gone atau 404 Not Found.
Melanjutkan upload
Jika permintaan Anda dihentikan sebelum menerima respons dari server atau jika Anda menerima respons 503 HTTP dari server, Anda dapat
membuat kueri status upload saat ini dengan mengajukan permintaan PUT kosong di URI upload unik.
Klien melakukan polling server untuk menentukan byte yang telah diterima:
PUT upload_uri HTTP/1.1 Host: docs.google.com Content-Length: 0 Content-Range: bytes */content_length
Gunakan * sebagai content_length jika panjangnya tidak diketahui.
Server merespons dengan rentang byte saat ini:
HTTP/1.1 308 Resume Incomplete Content-Length: 0 Range: bytes=0-42
Catatan: Jika server belum melakukan byte apa pun untuk sesi, server akan menghilangkan header Range.
Catatan: Server dapat mengeluarkan URI upload unik baru di header Location selama chunk. Klien Anda harus memeriksa Location yang telah diupdate dan menggunakan URI tersebut untuk mengirimkan potongan yang tersisa ke server.
Terakhir, klien melanjutkan dari tempat server berhenti:
PUT upload_uri HTTP/1.1 Host: docs.google.com Content-Length: 57 Content-Range: 43-99/100 <bytes 43-99>
Membatalkan upload
Jika Anda ingin membatalkan upload dan mencegah tindakan lebih lanjut pada upload tersebut, ajukan permintaan DELETE pada URI upload unik.
DELETE upload_uri HTTP/1.1 Host: docs.google.com Content-Length: 0
Jika berhasil, server akan merespons bahwa sesi dibatalkan, dan merespons dengan kode yang sama
untuk PUT lebih lanjut atau permintaan status kueri:
HTTP/1.1 499 Client Closed Request
Catatan: Jika upload dibatalkan tanpa pembatalan, masa berlakunya akan berakhir secara otomatis satu minggu setelah dibuat.
Memperbarui resource yang ada
Mirip dengan memulai sesi upload yang dapat dilanjutkan, Anda dapat menggunakan
protokol upload yang dapat dilanjutkan untuk mengganti konten file yang ada. Untuk memulai permintaan update yang dapat dilanjutkan,
kirim HTTP PUT ke link entri dengan rel='...#resumable-edit-media'.
Setiap media entry akan berisi link tersebut jika API mendukung update konten resource.
Sebagai contoh, entri dokumen di DocList API akan berisi link yang mirip dengan:
<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"/>
Dengan demikian, permintaan awalnya adalah:
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
Untuk memperbarui metadata dan konten resource secara bersamaan, sertakan Atom XML, bukan isi kosong. Lihat contoh di bagian Memulai permintaan upload yang dapat dilanjutkan.
Saat server merespons dengan URI upload unik, kirim PUT dengan payload Anda. Setelah Anda memiliki URI upload unik, proses untuk memperbarui konten file sama dengan mengupload file.
Contoh khusus ini akan memperbarui konten dokumen yang ada dalam satu kali pengambilan:
PUT upload_uri HTTP/1.1 Host: docs.google.com Content-Length: 1000 Content-Range: 0-999/1000 <bytes 0-999>
Contoh library klien
Di bawah ini adalah contoh mengupload file film ke Google Dokumen (menggunakan protokol upload yang dapat dilanjutkan) di library klien Google Data. Perhatikan bahwa tidak semua library mendukung fitur yang dapat dilanjutkan saat ini.
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
Untuk contoh lengkap dan referensi kode sumber, lihat referensi berikut:
- Aplikasi contoh dan sumber library Java
- Aplikasi contoh library Objective-C
- Sumber library .NET