এরিক বিডেলম্যান, জি স্যুট এপিআই টিম
ফেব্রুয়ারী ২০১০
ভূমিকা
বর্তমান ওয়েব স্ট্যান্ডার্ডগুলিতে বড় ফাইলের HTTP আপলোড সহজতর করার জন্য কোনও নির্ভরযোগ্য ব্যবস্থা নেই। ফলস্বরূপ, Google এবং অন্যান্য সাইটগুলিতে ফাইল আপলোড ঐতিহ্যগতভাবে মাঝারি আকারের (যেমন 100 MB) মধ্যে সীমাবদ্ধ। YouTube এবং Google Documents List API-এর মতো পরিষেবাগুলির জন্য যা বড় ফাইল আপলোড সমর্থন করে, এটি একটি বড় বাধা উপস্থাপন করে।
গুগল ডেটা রিজিউমেবল প্রোটোকল HTTP/1.0-এ রিজিউমেবল POST/PUT HTTP অনুরোধগুলিকে সমর্থন করে উপরে উল্লিখিত সমস্যাগুলি সরাসরি সমাধান করে। প্রোটোকলটি গুগল গিয়ার্স টিমের প্রস্তাবিত রিজিউমেবলHttpRequestsProposal-এর অনুকরণে তৈরি করা হয়েছে।
এই ডকুমেন্টে আপনার অ্যাপ্লিকেশনগুলিতে Google Data-এর পুনঃসূচনাযোগ্য আপলোড বৈশিষ্ট্যটি কীভাবে অন্তর্ভুক্ত করবেন তা বর্ণনা করা হয়েছে। নীচের উদাহরণগুলি Google Documents List Data API ব্যবহার করে। মনে রাখবেন যে এই প্রোটোকলটি বাস্তবায়নকারী অতিরিক্ত Google API-এর প্রয়োজনীয়তা/প্রতিক্রিয়া কোড/ইত্যাদি কিছুটা ভিন্ন হতে পারে। সুনির্দিষ্ট তথ্যের জন্য অনুগ্রহ করে পরিষেবার ডকুমেন্টেশন দেখুন।
পুনঃসূচনাযোগ্য প্রোটোকল
একটি পুনঃসূচনাযোগ্য আপলোড অনুরোধ শুরু করা হচ্ছে
একটি পুনঃসূচনাযোগ্য আপলোড সেশন শুরু করতে, পুনঃসূচনাযোগ্য-পোস্ট লিঙ্কে একটি HTTP 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 হেডারটি বাদ দেওয়া যেতে পারে।
এখানে আরেকটি উদাহরণ দেওয়া হল যেখানে একটি ওয়ার্ড ডকুমেন্ট আপলোড করা হয়। এবার, অ্যাটম মেটাডেটা অন্তর্ভুক্ত করা হয়েছে এবং এটি চূড়ান্ত ডকুমেন্ট এন্ট্রিতে প্রয়োগ করা হবে।
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 হেডারে একটি অনন্য আপলোড URI এবং একটি খালি প্রতিক্রিয়া বডি:
HTTP/1.1 200 OK
Location: <upload_uri>
ফাইলের অংশগুলি আপলোড করার জন্য অনন্য আপলোড URI ব্যবহার করা হবে।
দ্রষ্টব্য : প্রাথমিক POST অনুরোধ ফিডে একটি নতুন এন্ট্রি তৈরি করে না। এটি কেবল তখনই ঘটে যখন সম্পূর্ণ আপলোড অপারেশন সম্পন্ন হয়।
দ্রষ্টব্য : একটি পুনঃসূচনাযোগ্য সেশন URI এক সপ্তাহ পরে মেয়াদ শেষ হয়ে যায়।
একটি ফাইল আপলোড করা হচ্ছে
রিজিউমেবল প্রোটোকল 'চাঙ্ক'-এ কন্টেন্ট আপলোড করার অনুমতি দেয়, কিন্তু প্রয়োজন হয় না, কারণ HTTP-তে অনুরোধের আকারের উপর কোনও অন্তর্নিহিত বিধিনিষেধ নেই। আপনার ক্লায়েন্ট তার চাঙ্ক আকার বেছে নিতে অথবা সম্পূর্ণ ফাইলটি আপলোড করতে স্বাধীন। এই উদাহরণটি একটি রিজিউমেবল PUT ইস্যু করার জন্য অনন্য আপলোড URI ব্যবহার করে। নিম্নলিখিত উদাহরণটি 1234567 বাইট PDF ফাইলের প্রথম 100000 বাইট পাঠায়:
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 । আপলোড সম্পূর্ণ না হওয়া পর্যন্ত, সার্ভারটি Range হেডারে একটি HTTP 308 Resume Incomplete এবং তার জানা বাইট রেঞ্জের সাথে প্রতিক্রিয়া জানাবে। পরবর্তী অংশটি কোথা থেকে শুরু করবেন তা নির্ধারণ করতে ক্লায়েন্টদের অবশ্যই Range হেডার ব্যবহার করতে হবে। অতএব, ধরে নিবেন না যে সার্ভারটি PUT অনুরোধে মূলত প্রেরিত সমস্ত বাইট পেয়েছে।
দ্রষ্টব্য : সার্ভারটি একটি চাঙ্কের সময় Location হেডারে একটি নতুন অনন্য আপলোড URI ইস্যু করতে পারে। আপনার ক্লায়েন্টের উচিত একটি আপডেট করা Location পরীক্ষা করা এবং সেই URI ব্যবহার করে বাকি চাঙ্কগুলি সার্ভারে পাঠানো।
আপলোড সম্পূর্ণ হলে, প্রতিক্রিয়াটি API-এর নন-রিজিউমেবল আপলোড মেকানিজম ব্যবহার করে আপলোড করা হয়েছে এমনই হবে। অর্থাৎ, সার্ভার দ্বারা তৈরি <atom:entry> সহ একটি 201 Created ফেরত পাঠানো হবে। পরবর্তী PUT গুলি অনন্য আপলোড URI-তে আপলোড সম্পন্ন হওয়ার পরে যে প্রতিক্রিয়াটি ফেরত দেওয়া হয়েছিল তার মতোই ফেরত দেবে। কিছু সময়ের পরে, প্রতিক্রিয়াটি 410 Gone অথবা 404 Not Found হবে।
আপলোড পুনরায় শুরু করা হচ্ছে
যদি সার্ভার থেকে কোনও প্রতিক্রিয়া পাওয়ার আগেই আপনার অনুরোধটি বন্ধ করে দেওয়া হয় অথবা আপনি যদি সার্ভার থেকে কোনও HTTP 503 প্রতিক্রিয়া পান, তাহলে আপনি অনন্য আপলোড URI-তে একটি খালি 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 হেডারে একটি নতুন অনন্য আপলোড URI ইস্যু করতে পারে। আপনার ক্লায়েন্টের উচিত একটি আপডেট করা Location পরীক্ষা করা এবং সেই URI ব্যবহার করে বাকি চাঙ্কগুলি সার্ভারে পাঠানো।
অবশেষে, ক্লায়েন্ট সার্ভার যেখানে ছেড়েছিল সেখান থেকে পুনরায় শুরু করে:
PUT upload_uri HTTP/1.1 Host: docs.google.com Content-Length: 57 Content-Range: 43-99/100 <bytes 43-99>
আপলোড বাতিল করা হচ্ছে
যদি আপনি আপলোডটি বাতিল করতে চান এবং এর উপর আর কোনও পদক্ষেপ রোধ করতে চান, তাহলে অনন্য আপলোড URI-তে একটি 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 ' সহ এন্ট্রির লিঙ্কে একটি HTTP PUT পাঠান। API যদি রিসোর্সের কন্টেন্ট আপডেট করতে সহায়তা করে তবে প্রতিটি মিডিয়া 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
একই সাথে কোনও রিসোর্সের মেটাডেটা এবং কন্টেন্ট আপডেট করতে, খালি বডির পরিবর্তে Atom XML অন্তর্ভুক্ত করুন। "পুনরায় শুরু করা আপলোড অনুরোধ" বিভাগে উদাহরণটি দেখুন।
যখন সার্ভারটি অনন্য আপলোড URI দিয়ে সাড়া দেয়, তখন আপনার পেলোড সহ একটি PUT পাঠান। একবার আপনার অনন্য আপলোড URI হয়ে গেলে, ফাইলের বিষয়বস্তু আপডেট করার প্রক্রিয়াটি একটি ফাইল আপলোড করার মতোই।
এই বিশেষ উদাহরণটি এক ঝটকায় বিদ্যমান ডকুমেন্টের বিষয়বস্তু আপডেট করবে:
PUT upload_uri HTTP/1.1 Host: docs.google.com Content-Length: 1000 Content-Range: 0-999/1000 <bytes 0-999>
ক্লায়েন্ট লাইব্রেরির উদাহরণ
গুগল ডেটা ক্লায়েন্ট লাইব্রেরিতে (রিজিউমেবল আপলোড প্রোটোকল ব্যবহার করে) গুগল ডক্সে একটি মুভি ফাইল আপলোড করার নমুনা নিচে দেওয়া হল। মনে রাখবেন, এই মুহূর্তে সব লাইব্রেরি রিজিউমেবল বৈশিষ্ট্যটি সমর্থন করে না।
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
সম্পূর্ণ নমুনা এবং সোর্স কোড রেফারেন্সের জন্য, নিম্নলিখিত রিসোর্সগুলি দেখুন:
- জাভা লাইব্রেরির নমুনা অ্যাপ এবং উৎস
- অবজেক্টিভ-সি লাইব্রেরির নমুনা অ্যাপ
- .NET লাইব্রেরি উৎস