Skip to content
This repository has been archived by the owner on Sep 3, 2020. It is now read-only.

Commit

Permalink
Fix bug with missing downloadURL for docs/sheets
Browse files Browse the repository at this point in the history
Google Docs + Sheets do not populate 'DownloadUrl', but
instead provide an map of exportLinks ie csv, docx, pptx,
txt etc. Simple heuristic to determine the url to export
as well as create the target extension to avoid clobbering
of the original file on a re-sync.
  • Loading branch information
Emmanuel Odeke committed Nov 14, 2014
1 parent 3ef6932 commit 81cfdd9
Show file tree
Hide file tree
Showing 4 changed files with 61 additions and 6 deletions.
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
50 changes: 46 additions & 4 deletions pull.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (
"fmt"
"io"
"os"
"strings"
"path/filepath"
"sync"
)
Expand All @@ -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.
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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 {
Expand All @@ -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
}
Expand Down
10 changes: 8 additions & 2 deletions remote.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down
4 changes: 4 additions & 0 deletions types.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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,
}
}

Expand Down

0 comments on commit 81cfdd9

Please sign in to comment.