2016-03-19 4 views
-2

リンクから画像を取得してディスクに保存するコードを以下のように作成した場合、画像データを渡す最も良い方法は何ですか?Goで応答本体(response.Body)を渡す効率的な方法は何ですか?

ioutil.ReadAll(res.Body)を使用して[]byteに変換すると思っていましたが、ドキュメントからスライスまたは配列が返されるかどうかはわかりませんが、それを渡すのは高価なようです。私もres.Body*io.ReadCloserタイプへのポインタを返そうとしましたが、指摘されたインターフェイスの.Close()メソッドを正しく呼び出す方法を理解できませんでした。

保存コードをFetchImageに移動すると、これを解決するのが最も簡単な方法ですが、可能であればこれらの部分を別々にしたいと思います。

type ImageData struct { 
    Data  io.ReadCloser 
    Name  string 
} 

func FetchImage(url string) (io.ReadCloser, error) { 
    res, err := http.Get(url) 
    if err != nil { 
     return nil, err 
    } 
    return res.Body, nil 
} 

func Save(data *ImageData) error { 
    defer data.Data.Close() 
    file, err := os.Create(data.Name) 
    defer file.Close() 
    if err != nil { 
     return err 
    } 
    _, err = io.Copy(file, data.Data) 
    if err != nil { 
     return err 
    } 
    return nil 
} 

func main() { 
    body, err := fetcher.FetchImage("https://imgur.com/asdf.jpg") 
    if err != nil { 
     panic(err) 
    } 
    imageData := ImageData{body, "asdf.jpg"} 
    saver := Saver{config.BaseDir, 1} 
    err = saver.Save(&imageData) 
    if err != nil { 
     panic(err) 
    } 
} 

さらに、私はこのコードに悪いように見えるものがあれば、非常に新しいです。私に知らせてください。

答えて

1

ioutil.ReadAllを使用してください。この関数はバイトスライスを返します。

スライスは効率的です。スライスはバッキングアレイへのポインタで、長さと容量です。

type ImageData struct { 
    Data  []byte 
    Name  string 
} 

func FetchImage(url string) ([]byte, error) { 
    res, err := http.Get(url) 
    if err != nil { 
     return nil, err 
    } 
    defer resp.Body.Close() 
    if res.StatusCode != 200 { 
     return nil, fmt.Errorf("%s: %d", url, res.StatusCode) 
    } 
    return ioutil.ReadAll(resp.Body) 
} 

func Save(data *ImageData) error { 
    file, err := os.Create(data.Name) 
    if err != nil { 
     return err 
    } 
    defer file.Close() 
    _, err := file.Write(data.Data) 
    return err 
} 

レスポンスボディを渡すこともできますが、注意してください。基礎となる接続を解放するには、応答本体を閉じなければなりません。質問のコードはレスポンスボディを閉じますが、応答ボディが閉じられた関数にレスポンスボディが渡されるため、見るのが難しいです。

+0

お返事ありがとうございます。しかし、少し冗長ではありません。なぜなら、res.Bodyがすでにそのインターフェースを満たしているときに、新しいReaderをio.Copyに渡す必要があるからです。 – Wilfred

+0

io.Copyを使用する代わりに直接書き込みを書き込みます。 'file.Write(data)' –

1

すべての配列にはサイズ指定子があり、[8]byteのようになります。スライスにはこれがなく、[]byteのように見えます。スライス内部では、データへのポインタ、スラ​​イスの長さ、およびスライスの容量が格納されます。これは64ビットシステム上のわずか24バイトなので、それを渡すことについて心配する必要はありません。

*ImageDataFetchImageから返すことをお勧めします。イメージの名前のようなメタデータが既に含まれているためです。

なぜインタフェースへのポインタを取ることができないのかについては、理由を説明する投稿here on SOがあります。

また、Saveの場合は、defer file.Close()でエラーを確認してください。エラーがあればファイルはnilになり、おそらくsegfaultになるので、これを入れ替えてください。

+0

応答に感謝します。 – Wilfred

関連する問題