From 502acc3c73e789e856a17b878889db4356fe898c Mon Sep 17 00:00:00 2001 From: Stefan Majewsky Date: Mon, 19 Feb 2018 15:03:56 +0100 Subject: add Object.Download(), DownloadedObject --- download.go | 83 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ object.go | 32 +++++++++++++++++++++++- 2 files changed, 114 insertions(+), 1 deletion(-) create mode 100644 download.go diff --git a/download.go b/download.go new file mode 100644 index 0000000..6515bbf --- /dev/null +++ b/download.go @@ -0,0 +1,83 @@ +/****************************************************************************** +* +* Copyright 2018 Stefan Majewsky +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +******************************************************************************/ + +package schwift + +import ( + "io" + "io/ioutil" +) + +//DownloadedObject is returned by Object.Download(). It wraps the io.ReadCloser +//from http.Response.Body with convenience methods for collecting the contents +//into a byte slice or string. +// +// var obj *swift.Object +// +// //Do NOT do this! +// reader, err := obj.Download(nil, nil).AsReadCloser() +// bytes, err := ioutil.ReadAll(reader) +// err := reader.Close() +// str := string(bytes) +// +// //Do this instead: +// str, err := obj.Download(nil, nil).AsString() +// +//Since the AsByteSlice and AsString method consume only the unread portion of +//the ReadCloser, and since they drain the ReadCloser irreversibly, the +//idiomatic way of using DownloadedObject is to call one of its members +//immediately, without storing the DownloadedObject instance in a variable +//first. +// +// var obj *swift.Object +// +// //Do NOT do this! +// downloaded := obj.Download(nil, nil) +// reader, err := downloaded.AsReadCloser() +// +// //Do this instead: +// reader, err := obj.Download(nil, nil).AsReadCloser() +type DownloadedObject struct { + r io.ReadCloser + err error +} + +//AsReadCloser returns an io.ReadCloser containing the contents of the +//downloaded object. +func (o DownloadedObject) AsReadCloser() (io.ReadCloser, error) { + return o.r, o.err +} + +//AsByteSlice collects the contents of this downloaded object into a byte slice. +func (o DownloadedObject) AsByteSlice() ([]byte, error) { + if o.err != nil { + return nil, o.err + } + slice, err := ioutil.ReadAll(o.r) + closeErr := o.r.Close() + if err == nil { + err = closeErr + } + return slice, closeErr +} + +//AsString collects the contents of this downloaded object into a string. +func (o DownloadedObject) AsString() (string, error) { + slice, err := o.AsByteSlice() + return string(slice), err +} diff --git a/object.go b/object.go index a2a68e1..83ada1d 100644 --- a/object.go +++ b/object.go @@ -262,5 +262,35 @@ func (o *Object) Invalidate() { o.headers = nil } -//TODO Object.Copy(), Object.Move(), Object.Download() +//Download retrieves the object's contents using a GET request. This returns a +//helper object which allows you to select whether you want an io.ReadCloser +//for reading the object contents progressively, or whether you want the object +//contents collected into a byte slice or string. +// +// reader, err := object.Download(nil, nil).AsReadCloser() +// +// buf, err := object.Download(nil, nil).AsByteSlice() +// +// str, err := object.Download(nil, nil).AsString() +// +func (o *Object) Download(headers ObjectHeaders, opts *RequestOptions) DownloadedObject { + resp, err := Request{ + Method: "GET", + ContainerName: o.c.name, + ObjectName: o.name, + Headers: headersToHTTP(headers), + Options: opts, + ExpectStatusCodes: []int{200}, + }.Do(o.c.a.client) + if err == nil { + headers := ObjectHeaders(headersFromHTTP(resp.Header)) + err = headers.Validate() + if err == nil { + o.headers = &headers + } + } + return DownloadedObject{resp.Body, err} +} + +//TODO Object.Copy(), Object.Move() //TODO provide a companion to Object.Upload() to connect it with content-generating functions where an io.Writer needs to be given -- cgit v1.2.3