Upload Files To Google Drive Using ColdFusion
Asked Answered
K

1

24

*NEW UPDATED FOR BETTER SECOND PART - NOW GETS TO "308 Resume Incomplete", even though file should be just one upload!

I am using the foundation of cfgoogle from Ray Camden. But Google has deprecated the code for document uploads. The new standard is Resumable Media Uploads.

I have this part working (up to and including the "Initiating a resumable upload request") in the above referenced Google document.

Calling Page:

<cfset application.cfc.Google                   = createObject('component','#path_cf_cfc#Google') />
<cfset application.cfc.GoogleDocs               = createObject('component','#path_cf_cfc#GoogleDocs') />

<cfset gtoken = application.cfc.GoogleDocs.authenticate(emailaddress,password)>

<CFSET testdoc = "a\filepath\documentname.doc">
<CFSET FileType = "application/msword">
<CFSET FileTitle = "test_001">

<cfset temp = application.cfc.GoogleDocs.upload_auth("#Application.Map.DocStorage##tv.testdoc#",FileType,FileTitle)>  

<CFSET uploadpath = Listgetat(Listgetat(temp.header,ListContains(temp.header,"https://docs.google.com","#chr(10)#"),"#chr(10)#"),2," ") >  

<cfset temp2 = application.cfc.GoogleDocs.upload_file("#Application.Map.DocStorage##tv.testdoc#",FileType,FileTitle,uploadpath)>

The code works up to and including the cfset temp line (getting the unique upload URI)

Here is the code for upload_auth:

<cffunction name="upload_auth" access="public" returnType="any" hint="I get a uniqu URI from Google API." output="false">
<cfargument name="myFile" type="string" required="true" hint="filepath to upload.">
<cfargument name="myType" type="string" required="true" hint="application/msword"> 
<cfargument name="myTitle" type="string" required="true" hint="name of doc"> 

<cfset GoogleUrl = "https://docs.google.com/feeds/upload/create-session/default/private/full">
<cfset GoogleVersion = 3> 
<cfset FileSize = createObject("java","java.io.File").init(myFile).length()>

<cfhttp url="#GoogleUrl#" method="post" result="diditwork" resolveurl="no">
<cfhttpparam type="header" name="Authorization" value="GoogleLogin auth=#getAuth(variables.docservice)#">
<cfhttpparam type="header" name="GData-Version" value="#GoogleVersion#">
<cfhttpparam type="header" name="Content-Length" value="0">
<cfhttpparam type="header" name="X-Upload-Content-Type" value="#myType#">
<cfhttpparam type="header" name="X-Upload-Content-Length" value="#FileSize#">
<cfhttpparam type="header" name="Slug" value="#myTitle#">

</cfhttp>

<cfreturn diditwork>
</cffunction>

OK - So Far So Good. But here is where it breaks down:

Running upload_file returns "308 Resume Incomplete" (A lest it's not a 400!) from Google. Arrgh!!

Here is the upload_file -

<cffunction name="upload_file" access="public" returnType="any" hint="I upload the document." output="false">
<cfargument name="myFile" type="string" required="true" hint="filepath to upload.">
<cfargument name="myType" type="string" required="true" hint="like application/msword"> 
<cfargument name="myTitle" type="string" required="true" hint="name of doc"> 
<cfargument name="myAuthPath" type="string" required="true" hint="call auth"> 

<cfset FileSize = GetFileInfo(myFile).size >
<CFSET tv.tostartwithzero = FileSize - 1>

<CFFILE action="read" file="#myfile#" variable="FileText">

<cfhttp url="#myAuthPath#" method="put" result="diditwork" resolveurl="no" multipart="yes" charset="utf-8" >
<cfhttpparam type="header" name="Authorization" value="GoogleLogin auth=#getAuth(variables.docservice)#">
<cfhttpparam type="header" name="GData-Version" value="#variables.GoogleVersion#">
<cfhttpparam type="header" name="Content-Length" value="#FileSize#">
<cfhttpparam type="header" name="Content-Range" value="bytes 0-#tv.tostartwithzero#/#FileSize#">
<cfhttpparam type="header" name="Content-Type" value="#myType#">

<cfhttpparam type="body" value="#trim(FileText)#">

</cfhttp>

<cfreturn diditwork>
</cffunction>

So, there we have it - where I am stuck. I can get the unique URI, but (maybe because it is late at night) I'm brain dead on what I am doing wrong otherwise to complete the file upload.

All help is appreciated.

Katz answered 2/1, 2013 at 10:1 Comment(3)
I notice that in the content-range header you're using the file size as read off disk, but in the body, you're sending trim(FileText), meaning that if the file contains leading or trailing spaces, the request body size will be smaller than the size you're claiming to send. You could try using len(trim(FileText)) to calculate the range and size your sending in the content-range and content-length headers?Slimsy
You could also try doing a FileReadBinary and sending that in the request body, unless there's a reason to be trimming the file contentSlimsy
Did you get this resolved? Found the following here. Upon receiving the content body of a resume request, the server may still not posses the complete byte range, requiring further action (e.g. additional requests) from the client. In such cases, the server SHOULD return status code 308 (Resume Incomplete) if it is still willing to continue the operation. By definition, the 308 (Resume Incomplete) response indicates that the client can rectify the current error condition by sending the bytes which the server is missingMarashio
Z
1

I strongly recommend taking a different approach here. There are two big issues with the path you're taking:

  • It appears the code is using the deprecated client login auth mechanism and is using the username/password instead of OAuth.
  • Using the deprecated documents list API instead of the newer Drive API.

Since you can call java, I'd suggest writing the upload code in plain Java then invoking it from your CF pages as needed.

Zenaidazenana answered 5/4, 2013 at 18:6 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.