Пример #1
0
package api_test

import (
	"testing"
	"time"

	"github.com/ncw/rclone/b2/api"
	"github.com/ncw/rclone/fstest"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

var (
	emptyT api.Timestamp
	t0     = api.Timestamp(fstest.Time("1970-01-01T01:01:01.123456789Z"))
	t0r    = api.Timestamp(fstest.Time("1970-01-01T01:01:01.123000000Z"))
	t1     = api.Timestamp(fstest.Time("2001-02-03T04:05:06.123000000Z"))
)

func TestTimestampMarshalJSON(t *testing.T) {
	resB, err := t0.MarshalJSON()
	res := string(resB)
	require.NoError(t, err)
	assert.Equal(t, "3661123", res)

	resB, err = t1.MarshalJSON()
	res = string(resB)
	require.NoError(t, err)
	assert.Equal(t, "981173106123", res)
}
Пример #2
0
// Update the object with the contents of the io.Reader, modTime and size
//
// The new object may have been created if an error is returned
func (o *Object) Update(in io.Reader, modTime time.Time, size int64) (err error) {
	// Open a temp file to copy the input
	fd, err := ioutil.TempFile("", "rclone-b2-")
	if err != nil {
		return err
	}
	_ = os.Remove(fd.Name()) // Delete the file - may not work on Windows
	defer func() {
		_ = fd.Close()           // Ignore error may have been closed already
		_ = os.Remove(fd.Name()) // Delete the file - may have been deleted already
	}()

	// Copy the input while calculating the sha1
	hash := sha1.New()
	teed := io.TeeReader(in, hash)
	n, err := io.Copy(fd, teed)
	if err != nil {
		return err
	}
	if n != size {
		return fmt.Errorf("Read %d bytes expecting %d", n, size)
	}
	calculatedSha1 := fmt.Sprintf("%x", hash.Sum(nil))

	// Rewind the temporary file
	_, err = fd.Seek(0, 0)
	if err != nil {
		return err
	}

	// Get upload URL
	UploadURL, AuthorizationToken, err := o.fs.getUploadURL()
	if err != nil {
		return err
	}

	// Headers for upload file
	//
	// Authorization
	// required
	// An upload authorization token, from b2_get_upload_url.
	//
	// X-Bz-File-Name
	// required
	//
	// The name of the file, in percent-encoded UTF-8. See Files for requirements on file names. See String Encoding.
	//
	// Content-Type
	// required
	//
	// The MIME type of the content of the file, which will be returned in
	// the Content-Type header when downloading the file. Use the
	// Content-Type b2/x-auto to automatically set the stored Content-Type
	// post upload. In the case where a file extension is absent or the
	// lookup fails, the Content-Type is set to application/octet-stream. The
	// Content-Type mappings can be purused here.
	//
	// X-Bz-Content-Sha1
	// required
	//
	// The SHA1 checksum of the content of the file. B2 will check this when
	// the file is uploaded, to make sure that the file arrived correctly. It
	// will be returned in the X-Bz-Content-Sha1 header when the file is
	// downloaded.
	//
	// X-Bz-Info-src_last_modified_millis
	// optional
	//
	// If the original source of the file being uploaded has a last modified
	// time concept, Backblaze recommends using this spelling of one of your
	// ten X-Bz-Info-* headers (see below). Using a standard spelling allows
	// different B2 clients and the B2 web user interface to interoperate
	// correctly. The value should be a base 10 number which represents a UTC
	// time when the original source file was last modified. It is a base 10
	// number of milliseconds since midnight, January 1, 1970 UTC. This fits
	// in a 64 bit integer such as the type "long" in the programming
	// language Java. It is intended to be compatible with Java's time
	// long. For example, it can be passed directly into the Java call
	// Date.setTime(long time).
	//
	// X-Bz-Info-*
	// optional
	//
	// Up to 10 of these headers may be present. The * part of the header
	// name is replace with the name of a custom field in the file
	// information stored with the file, and the value is an arbitrary UTF-8
	// string, percent-encoded. The same info headers sent with the upload
	// will be returned with the download.

	opts := rest.Opts{
		Method:   "POST",
		Absolute: true,
		Path:     UploadURL,
		Body:     fd,
		ExtraHeaders: map[string]string{
			"Authorization":  AuthorizationToken,
			"X-Bz-File-Name": urlEncode(o.fs.root + o.remote),
			"Content-Type":   fs.MimeType(o),
			sha1Header:       calculatedSha1,
			timeHeader:       timeString(modTime),
		},
		ContentLength: &size,
	}
	var response api.FileInfo
	_, err = o.fs.srv.CallJSON(&opts, nil, &response)
	if err != nil {
		return fmt.Errorf("Failed to upload: %v", err)
	}
	o.info.ID = response.ID
	o.info.Name = response.Name
	o.info.Action = "upload"
	o.info.Size = response.Size
	o.info.UploadTimestamp = api.Timestamp(time.Now()) // FIXME not quite right
	return nil
}