diff --git a/README.md b/README.md index 94698312..6538068d 100644 --- a/README.md +++ b/README.md @@ -49,6 +49,9 @@ Background sync is not just hard, it's stupid. My technical and philosophical ra * Probably, it doesn't work on Windows. * Google Drive allows a directory to contain files/directories with the same name. Client doesn't handle these cases yet. We don't recommend you to use `drive` if you have such files/directories to avoid data loss. * Racing conditions occur if remote is being modified while we're trying to update the file. Google Drive provides resource versioning with ETags, use Etags to avoid racy cases. +* Google Docs + Sheets + Presentations data cannot be downloaded raw but only +as exported to different forms e.g docx, xlsx, csv etc hence doing a pull of +these types will result in a exported document. ## License Copyright 2013 Google Inc. All Rights Reserved. diff --git a/pull.go b/pull.go index 2785d2b1..64792663 100644 --- a/pull.go +++ b/pull.go @@ -18,6 +18,7 @@ import ( "fmt" "io" "os" + "strings" "path/filepath" "sync" ) @@ -26,6 +27,22 @@ const ( maxNumOfConcPullTasks = 4 ) +func docExportsMap() *map[string][]string { + return &map[string][]string { + "text/plain": []string{"text/plain", "txt",}, + "application/vnd.google-apps.drawing": []string{"image/svg+xml", "svg+xml",}, + "application/vnd.google-apps.spreadsheet": []string{ + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", "xlsx", + }, + "application/vnd.google-apps.document": []string{ + "application/vnd.openxmlformats-officedocument.wordprocessingml.document", "docx", + }, + "application/vnd.google-apps.presentation": []string{ + "application/vnd.openxmlformats-officedocument.presentationml.presentation", "pptx", + }, + } +} + // Pull from remote if remote path exists and in a god context. If path is a // directory, it recursively pulls from the remote if there are remote changes. // It doesn't check if there are remote changes if isForce is set. @@ -90,7 +107,8 @@ func (g *Commands) localMod(wg *sync.WaitGroup, change *Change) (err error) { defer g.taskDone() defer wg.Done() destAbsPath := g.context.AbsPathOf(change.Path) - if change.Src.BlobAt != "" { + + if change.Src.BlobAt != "" || change.Src.ExportLinks != nil { // download and replace if err = g.download(change); err != nil { return @@ -108,7 +126,7 @@ func (g *Commands) localAdd(wg *sync.WaitGroup, change *Change) (err error) { if change.Src.IsDir { return os.Mkdir(destAbsPath, os.ModeDir|0755) } - if change.Src.BlobAt != "" { + if change.Src.BlobAt != "" || change.Src.ExportLinks != nil { // download and create if err = g.download(change); err != nil { return @@ -124,7 +142,31 @@ func (g *Commands) localDelete(wg *sync.WaitGroup, change *Change) (err error) { } func (g *Commands) download(change *Change) (err error) { - destAbsPath := g.context.AbsPathOf(change.Path) + exportUrl := "" + baseName := change.Path + + // If BlobAt is not set, we are most likely dealing with + // Document/SpreadSheet/Image. In this case we'll use the target + // exportable type since we cannot directly download the raw data. + // We also need to pay attention and add the exported extension + // to avoid overriding the original file on re-syncing. + if len(change.Src.BlobAt) < 1 { + var ok bool + var mimeKeyExtList[]string + + exportsMap := *docExportsMap() + mimeKeyExtList, ok = exportsMap[change.Src.MimeType] + if !ok { + mimeKeyExtList = []string{"text/plain", "txt"} + } + + exportUrl = change.Src.ExportLinks[mimeKeyExtList[0]] + fmt.Print("Exported ", baseName) + baseName = strings.Join([]string{baseName, mimeKeyExtList[1]}, ".") + fmt.Println(" to: ", baseName) + } + + destAbsPath := g.context.AbsPathOf(baseName) var fo *os.File fo, err = os.Create(destAbsPath) if err != nil { @@ -144,7 +186,7 @@ func (g *Commands) download(change *Change) (err error) { blob.Close() } }() - blob, err = g.rem.Download(change.Src.Id) + blob, err = g.rem.Download(change.Src.Id, exportUrl) if err != nil { return err } diff --git a/remote.go b/remote.go index 8b4d6ce9..4cdfec0e 100644 --- a/remote.go +++ b/remote.go @@ -120,8 +120,14 @@ func (r *Remote) Publish(id string) (string, error) { return "https://googledrive.com/host/" + id, nil } -func (r *Remote) Download(id string) (io.ReadCloser, error) { - resp, err := r.transport.Client().Get("https://googledrive.com/host/" + id) +func (r *Remote) Download(id string, exportUrl string) (io.ReadCloser, error) { + var url string + if len(exportUrl) < 1 { + url = "https://googledrive.com/host/" + id + } else { + url = exportUrl + } + resp, err := r.transport.Client().Get(url) if err != nil || resp.StatusCode < 200 || resp.StatusCode > 299 { return resp.Body, err } diff --git a/types.go b/types.go index 2acad24c..d0e25722 100644 --- a/types.go +++ b/types.go @@ -38,7 +38,9 @@ type File struct { ModTime time.Time Size int64 BlobAt string + MimeType string Md5Checksum string + ExportLinks map[string]string } func NewRemoteFile(f *drive.File) *File { @@ -50,8 +52,10 @@ func NewRemoteFile(f *drive.File) *File { IsDir: f.MimeType == "application/vnd.google-apps.folder", ModTime: mtime, Size: f.FileSize, + MimeType: f.MimeType, BlobAt: f.DownloadUrl, Md5Checksum: f.Md5Checksum, + ExportLinks: f.ExportLinks, } }