एरिक बिडेलमैन, G Suite APIs टीम
फ़रवरी 2010
परिचय
मौजूदा वेब स्टैंडर्ड में, बड़ी फ़ाइलों को एचटीटीपी पर अपलोड करने की सुविधा देने के लिए कोई भरोसेमंद तरीका उपलब्ध नहीं है. इस वजह से, Google और अन्य साइटों पर फ़ाइलें अपलोड करने के लिए, साइज़ की सीमा तय की गई है. जैसे, 100 एमबी. YouTube और Google Documents List API जैसी सेवाओं के लिए, बड़ी फ़ाइलें अपलोड करने की सुविधा उपलब्ध है. हालांकि, इस वजह से उन्हें काफ़ी मुश्किलों का सामना करना पड़ता है.
Google Data का फिर से शुरू किया जा सकने वाला प्रोटोकॉल, ऊपर बताई गई समस्याओं को सीधे तौर पर हल करता है. इसके लिए, यह HTTP/1.0 में फिर से शुरू किए जा सकने वाले POST/PUT एचटीटीपी अनुरोधों का इस्तेमाल करता है. इस प्रोटोकॉल को Google Gears टीम के सुझाए गए ResumableHttpRequestsProposal के आधार पर बनाया गया था.
इस दस्तावेज़ में बताया गया है कि Google Data की फिर से शुरू होने वाले अपलोड की सुविधा को अपने ऐप्लिकेशन में कैसे शामिल करें. यहां दिए गए उदाहरणों में, Google Documents List Data API का इस्तेमाल किया गया है. ध्यान दें कि इस प्रोटोकॉल को लागू करने वाले अन्य Google API के लिए, कुछ अलग ज़रूरी शर्तें/जवाब कोड/वगैरह हो सकते हैं. ज़्यादा जानकारी के लिए, कृपया सेवा का दस्तावेज़ देखें.
फिर से शुरू किया जा सकने वाला प्रोटोकॉल
फिर से शुरू किए जा सकने वाले अपलोड के लिए अनुरोध करना
फिर से शुरू किए जा सकने वाले अपलोड सेशन को शुरू करने के लिए, फिर से शुरू किए जा सकने वाले पोस्ट लिंक पर एचटीटीपी POST अनुरोध भेजें. यह लिंक, फ़ीड लेवल पर मौजूद होता है.
DocList API का फिर से शुरू किया जा सकने वाला पोस्ट लिंक ऐसा दिखता है:
<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 अनुरोध की बॉडी खाली होनी चाहिए या इसमें Atom XML एंट्री होनी चाहिए. साथ ही, इसमें फ़ाइल का असल कॉन्टेंट शामिल नहीं होना चाहिए.
यहां दिए गए उदाहरण में, बड़े साइज़ की PDF फ़ाइल अपलोड करने के लिए फिर से शुरू किया जा सकने वाला अनुरोध बनाया गया है. इसमें 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
X-Upload-Content-Type और X-Upload-Content-Length हेडर को, उस फ़ाइल के माइम टाइप और साइज़ पर सेट किया जाना चाहिए जिसे आपको अपलोड करना है. अगर अपलोड सेशन बनाते समय कॉन्टेंट की अवधि के बारे में जानकारी नहीं है, तो X-Upload-Content-Length हेडर को शामिल न करें.
यहां अनुरोध का एक और उदाहरण दिया गया है, जिसमें Word दस्तावेज़ अपलोड किया गया है. इस बार, Atom मेटाडेटा शामिल किया गया है. इसे दस्तावेज़ की आखिरी एंट्री पर लागू किया जाएगा.
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>
शुरुआती POST से सर्वर का जवाब, Location हेडर में एक यूनीक अपलोड यूआरआई होता है. साथ ही, रिस्पॉन्स बॉडी खाली होती है:
HTTP/1.1 200 OK
Location: <upload_uri>
फ़ाइल के हिस्सों को अपलोड करने के लिए, यूनीक अपलोड यूआरआई का इस्तेमाल किया जाएगा.
ध्यान दें: शुरुआती POST अनुरोध से, फ़ीड में नई एंट्री नहीं बनती है.
ऐसा सिर्फ़ तब होता है, जब अपलोड करने की पूरी प्रोसेस पूरी हो जाती है.
ध्यान दें: फिर से शुरू किए जा सकने वाले सेशन का यूआरआई, एक हफ़्ते के बाद खत्म हो जाता है.
कोई फ़ाइल अपलोड करना
फिर से शुरू किए जा सकने वाले प्रोटोकॉल में, कॉन्टेंट को 'चंक' में अपलोड करने की अनुमति होती है. हालांकि, ऐसा करना ज़रूरी नहीं होता, क्योंकि एचटीटीपी में अनुरोध के साइज़ पर कोई पाबंदी नहीं होती. आपका क्लाइंट, फ़ाइल के साइज़ के हिसाब से उसे कई हिस्सों में बांट सकता है या पूरी फ़ाइल को एक साथ अपलोड कर सकता है.
इस उदाहरण में, फिर से शुरू किए जा सकने वाले PUT को जारी करने के लिए, अपलोड करने के यूनीक यूआरआई का इस्तेमाल किया गया है. यहां दिए गए उदाहरण में, 1234567 बाइट की PDF फ़ाइल के पहले 1,00,000 बाइट भेजे गए हैं:
PUT upload_uri HTTP/1.1 Host: docs.google.com Content-Length: 100000 Content-Range: bytes 0-99999/1234567 bytes 0-99999
अगर PDF फ़ाइल का साइज़ पता नहीं है, तो इस उदाहरण में Content-Range: bytes
0-99999/* का इस्तेमाल किया जाएगा. Content-Range हेडर के बारे में ज़्यादा जानकारी के लिए, यहां क्लिक करें.
सर्वर, सेव की गई बाइट की मौजूदा रेंज के साथ जवाब देता है:
HTTP/1.1 308 Resume Incomplete Content-Length: 0 Range: bytes=0-99999
आपके क्लाइंट को फ़ाइल के हर हिस्से को तब तक PUT करना चाहिए, जब तक पूरी फ़ाइल अपलोड न हो जाए.
अपलोड पूरा होने तक, सर्वर 308 Resume Incomplete एचटीटीपी स्टेटस कोड के साथ जवाब देगा. साथ ही, Range हेडर में बाइट रेंज की जानकारी देगा. क्लाइंट को Range हेडर का इस्तेमाल करना होगा, ताकि यह तय किया जा सके कि अगला हिस्सा कहां से शुरू करना है.
इसलिए, यह न मान लें कि सर्वर को PUT अनुरोध में भेजे गए सभी बाइट मिल गए हैं.
ध्यान दें: सर्वर, चंक के दौरान Location हेडर में नया यूनीक अपलोड यूआरआई जारी कर सकता है. आपके क्लाइंट को अपडेट किए गए Location की जांच करनी चाहिए. साथ ही, सर्वर को बाकी चंक भेजने के लिए उस यूआरआई का इस्तेमाल करना चाहिए.
अपलोड पूरा होने पर, जवाब वैसा ही होगा जैसा कि एपीआई के नॉन-रिज़्यूमेबल अपलोड सिस्टम का इस्तेमाल करके अपलोड करने पर मिलता है. इसका मतलब है कि सर्वर से बनाए गए <atom:entry> के साथ-साथ 201 Created भी वापस भेजा जाएगा. अपलोड किए गए यूनीक यूआरआई के लिए बाद में किए गए PUT अनुरोधों से, वही जवाब मिलेगा जो अपलोड पूरा होने पर मिला था.
कुछ समय बाद, जवाब 410 Gone या 404 Not Found होगा.
अपलोड फिर से शुरू करना
अगर सर्वर से जवाब मिलने से पहले ही आपका अनुरोध बंद कर दिया जाता है या आपको सर्वर से एचटीटीपी 503 जवाब मिलता है, तो यूनीक अपलोड यूआरआई पर खाली PUT अनुरोध भेजकर, अपलोड के मौजूदा स्टेटस के बारे में क्वेरी की जा सकती है.
क्लाइंट, सर्वर से यह पता लगाने के लिए पोल करता है कि उसे कौनसी बाइट मिली हैं:
PUT upload_uri HTTP/1.1 Host: docs.google.com Content-Length: 0 Content-Range: bytes */content_length
अगर लंबाई के बारे में जानकारी नहीं है, तो * को content_length के तौर पर इस्तेमाल करें.
सर्वर, बाइट की मौजूदा रेंज के साथ जवाब देता है:
HTTP/1.1 308 Resume Incomplete Content-Length: 0 Range: bytes=0-42
ध्यान दें: अगर सर्वर ने सेशन के लिए कोई बाइट नहीं भेजी है, तो वह Range हेडर को हटा देगा.
ध्यान दें: सर्वर, चंक के दौरान Location हेडर में नया यूनीक अपलोड यूआरआई जारी कर सकता है. आपके क्लाइंट को अपडेट किए गए Location की जांच करनी चाहिए. साथ ही, सर्वर को बाकी चंक भेजने के लिए उस यूआरआई का इस्तेमाल करना चाहिए.
आखिर में, क्लाइंट उस जगह से काम शुरू करता है जहां सर्वर ने छोड़ा था:
PUT upload_uri HTTP/1.1 Host: docs.google.com Content-Length: 57 Content-Range: 43-99/100 <bytes 43-99>
अपलोड करने की प्रोसेस रद्द करना
अगर आपको अपलोड रद्द करना है और इस पर कोई और कार्रवाई नहीं करनी है, तो DELETE यूनीक अपलोड यूआरआई पर अनुरोध करें.
DELETE upload_uri HTTP/1.1 Host: docs.google.com Content-Length: 0
अगर सेशन रद्द हो जाता है, तो सर्वर जवाब देता है कि सेशन रद्द हो गया है. साथ ही, आगे के PUT या क्वेरी स्टेटस के अनुरोधों के लिए, सर्वर इसी कोड के साथ जवाब देता है:
HTTP/1.1 499 Client Closed Request
ध्यान दें: अगर अपलोड किए जा रहे डेटा को रद्द किए बिना छोड़ दिया जाता है, तो वह एक हफ़्ते बाद अपने-आप मिट जाता है.
किसी मौजूदा संसाधन को अपडेट करना
फिर से अपलोड किए जा सकने वाले सेशन को शुरू करने की तरह ही, किसी मौजूदा फ़ाइल के कॉन्टेंट को बदलने के लिए, फिर से अपलोड किए जा सकने वाले प्रोटोकॉल का इस्तेमाल किया जा सकता है. फिर से शुरू किए जा सकने वाले अपडेट का अनुरोध शुरू करने के लिए, rel='...#resumable-edit-media' के साथ एंट्री के लिंक पर एचटीटीपी PUT भेजें.
अगर एपीआई, संसाधन के कॉन्टेंट को अपडेट करने की सुविधा देता है, तो हर मीडिया entry में ऐसा लिंक शामिल होगा.
उदाहरण के लिए, DocList API में किसी दस्तावेज़ की एंट्री में इस तरह का लिंक शामिल होगा:
<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"/>
इसलिए, शुरुआती अनुरोध यह होगा:
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
किसी संसाधन के मेटाडेटा और कॉन्टेंट को एक साथ अपडेट करने के लिए, खाली बॉडी के बजाय ऐटम एक्सएमएल शामिल करें. फिर से शुरू किए जा सकने वाले अपलोड अनुरोध को शुरू करना सेक्शन में दिया गया उदाहरण देखें.
जब सर्वर, अपलोड करने के लिए यूनीक यूआरआई का जवाब देता है, तब अपने पेलोड के साथ PUT भेजें. यूनीक अपलोड यूआरआई मिलने के बाद, फ़ाइल के कॉन्टेंट को अपडेट करने की प्रोसेस वही होती है जो फ़ाइल अपलोड करने के लिए इस्तेमाल की जाती है.
इस उदाहरण में, मौजूदा दस्तावेज़ के कॉन्टेंट को एक ही बार में अपडेट किया जाएगा:
PUT upload_uri HTTP/1.1 Host: docs.google.com Content-Length: 1000 Content-Range: 0-999/1000 <bytes 0-999>
क्लाइंट लाइब्रेरी के उदाहरण
यहां Google Data क्लाइंट लाइब्रेरी में, फिर से शुरू किए जा सकने वाले अपलोड प्रोटोकॉल का इस्तेमाल करके, Google Docs में किसी मूवी फ़ाइल को अपलोड करने के उदाहरण दिए गए हैं. ध्यान दें कि फ़िलहाल, सभी लाइब्रेरी में फिर से शुरू करने की सुविधा उपलब्ध नहीं है.
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
पूरे सैंपल और सोर्स कोड के रेफ़रंस के लिए, यहां दिए गए संसाधन देखें:
- Java लाइब्रेरी का सैंपल ऐप्लिकेशन और सोर्स
- Objective-C लाइब्रेरी का सैंपल ऐप्लिकेशन
- .NET लाइब्रेरी का सोर्स