onedrive: fix upload of zero length files #1716

Unfortunately multi part upload can't upload zero length files so
bring back the single part upload for zero length files only.

This was broken when we made all uploads multipart uploads.
This commit is contained in:
Nick Craig-Wood 2018-03-17 09:46:06 +00:00
parent a9e386b153
commit d4cca8d9f9

View File

@ -1112,7 +1112,7 @@ func (o *Object) createUploadSession(modTime time.Time) (response *api.CreateUpl
} }
// uploadFragment uploads a part // uploadFragment uploads a part
func (o *Object) uploadFragment(url string, start int64, totalSize int64, chunk io.ReadSeeker, chunkSize int64, info *api.Item) (err error) { func (o *Object) uploadFragment(url string, start int64, totalSize int64, chunk io.ReadSeeker, chunkSize int64) (info *api.Item, err error) {
opts := rest.Opts{ opts := rest.Opts{
Method: "PUT", Method: "PUT",
RootURL: url, RootURL: url,
@ -1131,12 +1131,13 @@ func (o *Object) uploadFragment(url string, start int64, totalSize int64, chunk
// we are done :) // we are done :)
// read the item // read the item
defer fs.CheckClose(resp.Body, &err) defer fs.CheckClose(resp.Body, &err)
info = &api.Item{}
return false, json.NewDecoder(resp.Body).Decode(info) return false, json.NewDecoder(resp.Body).Decode(info)
} }
} }
return retry, err return retry, err
}) })
return err return info, err
} }
// cancelUploadSession cancels an upload session // cancelUploadSession cancels an upload session
@ -1155,16 +1156,16 @@ func (o *Object) cancelUploadSession(url string) (err error) {
} }
// uploadMultipart uploads a file using multipart upload // uploadMultipart uploads a file using multipart upload
func (o *Object) uploadMultipart(in io.Reader, size int64, modTime time.Time, info *api.Item) (err error) { func (o *Object) uploadMultipart(in io.Reader, size int64, modTime time.Time) (info *api.Item, err error) {
if chunkSize%(320*1024) != 0 { if chunkSize%(320*1024) != 0 {
return errors.Errorf("chunk size %d is not a multiple of 320k", chunkSize) return nil, errors.Errorf("chunk size %d is not a multiple of 320k", chunkSize)
} }
// Create upload session // Create upload session
fs.Debugf(o, "Starting multipart upload") fs.Debugf(o, "Starting multipart upload")
session, err := o.createUploadSession(modTime) session, err := o.createUploadSession(modTime)
if err != nil { if err != nil {
return err return nil, err
} }
uploadURL := session.UploadURL uploadURL := session.UploadURL
@ -1189,15 +1190,44 @@ func (o *Object) uploadMultipart(in io.Reader, size int64, modTime time.Time, in
} }
seg := readers.NewRepeatableReader(io.LimitReader(in, n)) seg := readers.NewRepeatableReader(io.LimitReader(in, n))
fs.Debugf(o, "Uploading segment %d/%d size %d", position, size, n) fs.Debugf(o, "Uploading segment %d/%d size %d", position, size, n)
err = o.uploadFragment(uploadURL, position, size, seg, n, info) info, err = o.uploadFragment(uploadURL, position, size, seg, n)
if err != nil { if err != nil {
return err return nil, err
} }
remaining -= n remaining -= n
position += n position += n
} }
return nil return info, nil
}
// uploadSinglepart uploads a file as a single part
func (o *Object) uploadSinglepart(in io.Reader, size int64, modTime time.Time) (info *api.Item, err error) {
var resp *http.Response
opts := rest.Opts{
Method: "PUT",
Path: "/root:/" + rest.URLPathEscape(o.srvPath()) + ":/content",
ContentLength: &size,
Body: in,
}
// for go1.8 (see release notes) we must nil the Body if we want a
// "Content-Length: 0" header which onedrive requires for all files.
if size == 0 {
opts.Body = nil
}
err = o.fs.pacer.CallNoRetry(func() (bool, error) {
resp, err = o.fs.srv.CallJSON(&opts, nil, &info)
return shouldRetry(resp, err)
})
if err != nil {
return nil, err
}
err = o.setMetaData(info)
if err != nil {
return nil, err
}
// Set the mod time now and read metadata
return o.setModTime(modTime)
} }
// Update the object with the contents of the io.Reader, modTime and size // Update the object with the contents of the io.Reader, modTime and size
@ -1210,8 +1240,13 @@ func (o *Object) Update(in io.Reader, src fs.ObjectInfo, options ...fs.OpenOptio
size := src.Size() size := src.Size()
modTime := src.ModTime() modTime := src.ModTime()
info := &api.Item{} var info *api.Item
err = o.uploadMultipart(in, size, modTime, info) if size <= 0 {
// This is for 0 length files, or files with an unknown size
info, err = o.uploadSinglepart(in, size, modTime)
} else {
info, err = o.uploadMultipart(in, size, modTime)
}
if err != nil { if err != nil {
return err return err
} }