// This multipart upload operation uploads a part of an archive.You can upload // archive parts in any order because in your Upload Part request you specify // the range of bytes in the assembled archive that will be uploaded in this // part.You can also upload these parts in parallel.You can upload up to 10,000 // parts for a multipart upload. // // Amazon Glacier rejects your upload part request if any of the following // conditions is true: // * SHA256 tree hash does not match—To ensure that part data is not corrupted // in transmission, you compute a SHA256 tree hash of the part and include it in // your request. Upon receiving the part data, Amazon Glacier also computes a // SHA256 tree hash. If the two hash values don't match, the operation fails. // * Part size does not match—The size of each part except the last must match // the size that is specified in the corresponding Initiate Multipart Upload. // The size of the last part must be the same size as, or smaller than, the // specified size. Note: If you upload a part whose size is smaller than the // part size you specified in your initiate multipart upload request and that // part is not the last part, then the upload part request will succeed. // However, the subsequent Complete Multipart Upload request will fail. // * Range does not align—The byte range value in the request does not align // with the part size specified in the corresponding initiate request. For // example, if you specify a part size of 4194304 bytes (4 MB), then 0 to // 4194303 bytes (4 MB —1) and 4194304 (4 MB) to 8388607 (8 MB —1) are valid // part ranges. However, if you set a range value of 2 MB to 6 MB, the range // does not align with the part size and the upload will fail. // // This operation is idempotent. If you upload the same part multiple times, the // data included in the most recent request overwrites the previously uploaded // data. func (c *Connection) UploadMultipart(vault, uploadId string, start int64, body io.ReadSeeker) error { // TODO check that data size and start location make sense // Build request. request, err := http.NewRequest("PUT", "https://"+c.Signature.Region.Glacier+"/-/vaults/"+vault+ "/multipart-uploads/"+uploadId, body) if err != nil { return err } request.Header.Add("x-amz-glacier-version", "2012-06-01") th := NewTreeHash() n, err := io.Copy(th, body) if err != nil { return err } th.Close() _, err = body.Seek(0, 0) if err != nil { return err } hash := th.Hash() request.Header.Add("x-amz-content-sha256", toHex(hash)) request.Header.Add("x-amz-sha256-tree-hash", toHex(th.TreeHash())) request.Header.Add("Content-Range", fmt.Sprintf("bytes %d-%d/*", start, start+n-1)) request.ContentLength = n c.Signature.Sign(request, aws.HashedPayload(hash)) // Perform request. response, err := c.client().Do(request) if err != nil { return err } defer response.Body.Close() if response.StatusCode != http.StatusNoContent { return aws.ParseError(response) } io.Copy(ioutil.Discard, response.Body) // Parse success response. return nil }
// Upload archive to vault with optional description. The entire archive will // be read in order to create its tree hash before uploading. // // Returns the archive ID or the first error encountered. func (c *Connection) UploadArchive(vault string, archive io.ReadSeeker, description string) (string, error) { // Build reuest. request, err := http.NewRequest("POST", c.vault(vault)+"/archives", archive) if err != nil { return "", err } request.Header.Add("x-amz-glacier-version", "2012-06-01") th := NewTreeHash() request.ContentLength, err = io.Copy(th, archive) if err != nil { return "", err } th.Close() _, err = archive.Seek(0, 0) if err != nil { return "", err } hash := th.Hash() request.Header.Add("x-amz-archive-description", description) request.Header.Add("x-amz-sha256-tree-hash", toHex(th.TreeHash())) request.Header.Add("x-amz-content-sha256", toHex(hash)) c.Signature.Sign(request, aws.HashedPayload(hash)) // Perform request. response, err := c.client().Do(request) if err != nil { return "", err } defer response.Body.Close() if response.StatusCode != http.StatusCreated { return "", aws.ParseError(response) } io.Copy(ioutil.Discard, response.Body) // Parse success response. _, location := path.Split(response.Header.Get("Location")) return location, nil }