Save an image from url to file - go

Very new to Go (first simple project I'm working on).
Question: How do I get an image from URL and then save it to my computer?
Here's what I have so far:
package main
import (
"fmt"
"net/http"
"image"
"io/ioutil"
)
func main() {
url := "http://i.imgur.com/m1UIjW1.jpg"
// don't worry about errors
response, _ := http.Get(url);
defer response.Body.Close()
m, _, err := image.Decode(response.Body)
error := ioutil.WriteFile("/images/asdf.jpg", m, 0644)
}
However, when I run this code, I get cannot use m (type image.Image) as type []byte in function argument
I'm assuming I have to convert image.Image (variable m) into an undefined amount of bytes? Is that the correct way to go about this?

There is no need to decode the file. Simply copy the response body to a file you've opened. Here's the deal in the modified example:
response.Body is a stream of data, and implements the Reader interface - meaning you can sequentially call Read on it, as if it was an open file.
The file I'm opening here implements the Writer interface. This is the opposite - it's a stream you can call Write on.
io.Copy "patches" a reader and a writer, consumes the reader stream and writes its contents to a Writer.
This is one of my favorite things about go - implicit interfaces. You don't have to declare you're implementing an interface, you just have to implement it to be used in some context. This allows mixing and matching of code that doesn't need to know about other code it's interacting with.
package main
import (
"fmt"
"io"
"log"
"net/http"
"os"
)
func main() {
url := "http://i.imgur.com/m1UIjW1.jpg"
// don't worry about errors
response, e := http.Get(url)
if e != nil {
log.Fatal(e)
}
defer response.Body.Close()
//open a file for writing
file, err := os.Create("/tmp/asdf.jpg")
if err != nil {
log.Fatal(err)
}
defer file.Close()
// Use io.Copy to just dump the response body to the file. This supports huge files
_, err = io.Copy(file, response.Body)
if err != nil {
log.Fatal(err)
}
fmt.Println("Success!")
}

package main
import (
"io"
"net/http"
"os"
"fmt"
)
func main() {
img, _ := os.Create("image.jpg")
defer img.Close()
resp, _ := http.Get("http://i.imgur.com/Dz2r9lk.jpg")
defer resp.Body.Close()
b, _ := io.Copy(img, resp.Body)
fmt.Println("File size: ", b)
}

Try this:
package main
import (
"fmt"
"net/http"
"net/url"
"os"
"strings"
)
var (
fileName string
fullUrlFile string
)
func main() {
fullUrlFile = "https://i.imgur.com/m1UIjW1.jpg"
r, e := http.Get(fullUrlFile)
if e != nil {
panic(e)
}
defer r.Body.Close()
buildFileName()
// Create distination
f, e := os.Create(fileName) // "m1UIjW1.jpg"
if e != nil {
panic(e)
}
defer f.Close()
// Fill distination with content
n, e := f.ReadFrom(r.Body)
if e != nil {
panic(e)
}
fmt.Println("File size: ", n)
}
func buildFileName() {
fileUrl, e := url.Parse(fullUrlFile)
if e != nil {
panic(e)
}
path := fileUrl.Path
segments := strings.Split(path, "/")
fileName = segments[len(segments)-1]
println(fileName)
}

You can also use the ReadFrom method:
package main
import (
"net/http"
"os"
)
func main() {
r, e := http.Get("https://i.imgur.com/m1UIjW1.jpg")
if e != nil {
panic(e)
}
defer r.Body.Close()
f, e := os.Create("m1UIjW1.jpg")
if e != nil {
panic(e)
}
defer f.Close()
f.ReadFrom(r.Body)
}
https://golang.org/pkg/os#File.ReadFrom

What is the type of response.Body? You should just convert that into a []byte if it is not and write that to disk. There is no reason to use the image class unless you have some reason to treat the data as an image. Just treat the data as a series of bytes and write it to the disk.

Related

Golang segment stdin stream to file

instead of writing a pipe to a huge file i want to segment the stream in chunks on signal USR1. i think i got the basics working but the app just hangs and nothing happens, any clues or best practices when handling with an uncontrollable input stream and byte perfect segmentation?
package main
import (
"bufio"
"fmt"
"io"
"os"
"os/signal"
"syscall"
"time"
)
var done bool
func handle(c chan os.Signal) {
for {
sig := <-c
switch sig {
case syscall.SIGUSR1:
fmt.Println("###Sink temporarily_closed###")
done = true
case syscall.SIGUSR2:
fmt.Println("###Sink closed###")
done = true
case syscall.SIGHUP:
fmt.Println("###Sink running###")
}
}
}
func check(e error) {
if e != nil {
panic(e)
}
}
func main() {
c := make(chan os.Signal, 1)
signal.Notify(c, syscall.SIGUSR1, syscall.SIGUSR2, syscall.SIGHUP)
go handle(c)
reader := bufio.NewReaderSize(os.Stdin,1024*10)
for true {
if done {
file, err := os.Create("./temp.file")
check(err)
writer := bufio.NewWriter(file)
written, err := io.Copy(writer,reader)
check(err)
fmt.Println(written)
writer.Flush()
file.Close()
reader.Reset(os.Stdin)
done = false
}
time.Sleep(time.Millisecond)
}
}
So you need to io.CopyN(dst, src, 4096) in the loop and rotate the file once in a while. See example. I made rotation by size but it is easy to add signal handling.
package main
import (
"fmt"
"io"
"log"
"os"
"time"
)
var count int
var f *os.File
func rotate() *os.File {
if f != nil {
if err := f.Close(); err != nil {
log.Fatal(err)
}
}
fname := fmt.Sprintf("./dump-%d.bin", count)
count++
f, err := os.Create(fname)
if err != nil {
log.Fatal(err)
}
log.Println("rotated:", fname)
return f
}
func main() {
var n, written int
reader := os.Stdin
for {
if written == 0 || written >= 4096*10 {
f = rotate()
written = 0
}
n, err := io.CopyN(f, reader, 4096)
if err != nil {
log.Fatal(err)
}
written += n
log.Println("written:", written)
time.Sleep(time.Millisecond * 500)
}
}

Semantic way of http.Response receiver functions in Go

I just started learning GO and wrote this piece of code that writes an http.Response.Body to os.Stdout or to a file, but I'm not happy about the semantics of this.
I want the http.Response struct to have these receiver functions, so I can use it more easily throughout the entire app.
I know that the answers might get flagged as opinionated, but I still wonder, is there a better way of writing this?
Is there some sort of best practice?
package main
import (
"fmt"
"io"
"io/ioutil"
"net/http"
"os"
)
type httpResp http.Response
func main() {
res, err := http.Get("http://www.stackoverflow.com")
if err != nil {
fmt.Println("Error: ", err)
os.Exit(1)
}
defer res.Body.Close()
response := httpResp(*res)
response.toFile("stckovrflw.html")
response.toStdOut()
}
func (r httpResp) toFile(filename string) {
str, err := ioutil.ReadAll(r.Body)
if err != nil {
panic(err)
}
ioutil.WriteFile(filename, []byte(str), 0666)
}
func (r httpResp) toStdOut() {
_, err := io.Copy(os.Stdout, r.Body)
if err != nil {
panic(err)
}
}
On a side note, is there a way to make the http.Get method spit out a custom type that already has access to these receiver functions without the need for casting? So i could do something like this:
func main() {
res, err := http.Get("http://www.stackoverflow.com")
if err != nil {
fmt.Println("Error: ", err)
os.Exit(1)
}
defer res.Body.Close()
res.toFile("stckovrflw.html")
res.toStdOut()
}
Thanks!
You don't have to implement these functions. *http.Response already implements io.Writer:
Write writes r to w in the HTTP/1.x server response format, including the status line, headers, body, and optional trailer.
package main
import (
"net/http"
"os"
)
func main() {
r := &http.Response{}
r.Write(os.Stdout)
}
In the example above, the zero value prints:
HTTP/0.0 000 status code 0
Content-Length: 0
Playground: https://play.golang.org/p/2AUEAUPCA8j
In case you need additional business logic in the write methods, you can embed *http.Response in your defined type:
type RespWrapper struct {
*http.Response
}
func (w *RespWrapper) toStdOut() {
_, err := io.Copy(os.Stdout, w.Body)
if err != nil {
panic(err)
}
}
But then you must construct a variable of type RespWrapper with the *http.Response:
func main() {
// resp with a fake body
r := &http.Response{Body: io.NopCloser(strings.NewReader("foo"))}
// or r, _ := http.Get("example.com")
// construct the wrapper
wrapper := &RespWrapper{Response: r}
wrapper.toStdOut()
}
is there a way to make the http.Get method spit out a custom type
No, the return types of http.Get are (resp *http.Response, err error), that's part of the function signature, you can't change it.

Different result when run io.Copy(os.Stdout, &r) consecutively in Golang

I am playing around Golang. About io.Copy
I put 2 consecutive io.Copy in the code, but i expect it output twice result(testtesttest). But the 2nd one is nil. Can anyone help explain why? tks
package main
import (
"io"
"os"
"strings"
"fmt"
)
type testReader struct {
w io.Reader
str string
}
func (tt *testReader) Read (b []byte) (n int, err error) {
io.Copy(os.Stdout, tt.w)
n, err = tt.w.Read(b)
if tt.w !=nil {
return 0,io.EOF
}
return
}
func main() {
s := strings.NewReader("testtesttest!!!")
r := testReader{s,"ttthhh"}
fmt.Println(&r)
io.Copy(os.Stdout, &r)
// s.Seek(0,0) // solution from Poy's answer
io.Copy(os.Stdout, &r)
}
I'm going to prune down the given example to (as there is a bit of noise):
package main
import (
"io"
"os"
"strings"
)
func main() {
s := strings.NewReader("testtesttest")
io.Copy(os.Stdout, s) // Will print "testtesttest"
io.Copy(os.Stdout, s) // Won't print anything
}
The reason the second copy won't output anything is the io.Reader (s) has already been read. Reading from a io.Reader is not idempotent (you can't call it twice to get the same results). It also doesn't have a way to "reset" it or anything.
As #JRLambert pointed out you have s.Seek() and s.Reset() to allow you to start reading again.
Quick addition to all the correct answers (#poy and #JRLambert) provided so far... Use io.TeeReader or io.MultiWriter for times when you would want to use io.Copy more than once. Below are some examples of using each.
Using io.TeeReader
package main
import (
"bytes"
"crypto/md5"
"encoding/hex"
"fmt"
"io"
"io/ioutil"
"os"
)
func main() {
sourceFile, _ := os.Open("source/ebook.pdf")
var buf bytes.Buffer
tee := io.TeeReader(sourceFile, &buf)
process := func(sourceReader io.Reader) {
targetFile, _ := os.Create("target/ebook.pdf")
defer targetFile.Close()
if _, err := io.Copy(targetFile, sourceReader); err != nil {
fmt.Println(err)
}
}
process(tee)
fmt.Println(checksum(&buf))
}
func checksum(buf *bytes.Buffer) string {
h := md5.New()
b, _ := ioutil.ReadAll(buf)
if _, err := h.Write(b); err != nil {
fmt.Println(err)
}
return hex.EncodeToString(h.Sum(nil)[:16])
}
Using io.MultiWriter
package main
import (
"bytes"
"crypto/md5"
"encoding/hex"
"fmt"
"io"
"io/ioutil"
"os"
)
func main() {
sourceFile, _ := os.Open("source/ebook.pdf")
process := func(sourceReader io.Reader) {
targetFile, _ := os.Create("target/ebook.pdf")
defer targetFile.Close()
var buf1, buf2 bytes.Buffer
w := io.MultiWriter(targetFile, &buf1, &buf2)
if _, err := io.Copy(w, sourceReader); err != nil {
fmt.Println(err)
}
fmt.Println(checksum(&buf1))
fmt.Println(checksum(&buf2))
}
process(sourceFile)
}
func checksum(buf *bytes.Buffer) string {
h := md5.New()
b, _ := ioutil.ReadAll(buf)
if _, err := h.Write(b); err != nil {
fmt.Println(err)
}
return hex.EncodeToString(h.Sum(nil)[:16])
}

Golang AES decrypt video before streaming it though http

Im trying to stream AES encrypted video file; however we need to decrypt it on the fly before streaming it though http
how to write the output of decrypted data to http writer
package main
import (
"crypto/aes"
"crypto/cipher"
"io"
"log"
"net/http"
"os"
)
var key []byte = []byte("yourAESPrivateKey")
func ServeHTTP(w http.ResponseWriter, r *http.Request) {
inFile, err := os.Open("2.ts")
if err != nil {
log.Fatal(err)
}
defer inFile.Close()
block, err := aes.NewCipher(key)
if err != nil {
log.Fatal(err)
return
}
var iv [aes.BlockSize]byte
stream := cipher.NewOFB(block, iv[:])
w.Header().Set("Content-type", "video/mp4")
writer := &cipher.StreamWriter{S: stream, W: w}
if _, err := io.Copy(writer, inFile); err != nil {
log.Fatal(err)
return
}
//http.ServeContent(w, r, "video.mp4", time.Now(), inFile)
}
func main() {
http.HandleFunc("/", ServeHTTP)
http.ListenAndServe(":2020", nil)
}
i have enhanced my code
so if i requested an encrypted file, the server will decrypt it and serve it properly, however i have another problem as below
the problem now is that i get corrupted file if file exceeds certain size.
i have done some tests on text files for debugging purposes and so i can post data results here (big corrupted file) check the end of the file
package main
import (
"crypto/aes"
"crypto/cipher"
"log"
"net/http"
"os"
"time"
)
type cipherHttpWriter struct {
http.ResponseWriter
}
func (c cipherHttpWriter) Write(b []byte) (int, error) {
var key []byte = []byte("we!#09bsa$.a-ala.HJOqweK45aghf&A")
block, err := aes.NewCipher(key)
if err != nil {
return 0, err
}
var iv [aes.BlockSize]byte
stream := cipher.NewOFB(block, iv[:])
streamWriter := &cipher.StreamWriter{S: stream, W: c.ResponseWriter}
defer streamWriter.Close()
return streamWriter.Write(b)
}
func ServeHTTP(w http.ResponseWriter, r *http.Request) {
inFile, err := os.Open(string(r.URL.Path[1:]))
if err != nil {
log.Fatal(err)
}
defer inFile.Close()
writer := cipherHttpWriter{}
writer.ResponseWriter = w
http.ServeContent(writer, r, "", time.Now(), inFile)
}
func main() {
http.HandleFunc("/", ServeHTTP)
http.ListenAndServe(":2020", nil)
}

Serve a decoded image via Martini

I am currently playing around with golang and Martini and such and wanted to dynamically serve some manipulated/generated images. Here's a minimal example:
package main
import (
"github.com/codegangsta/martini"
"github.com/nfnt/resize"
"image"
"image/jpeg"
"log"
"os"
)
func thumb() image.Image {
file, err := os.Open("test.jpg")
if err != nil {
log.Fatal(err)
}
img, err := jpeg.Decode(file)
if err != nil {
log.Fatal(err)
}
file.Close()
m := resize.Resize(0, 200, img, resize.MitchellNetravali)
return m
}
func main() {
m := martini.Classic()
m.Get("/") image.Image {
return thumb()
})
m.Run()
}
That compiles fine, but instead of serving an image, I get some "Content-Type:text/plain; charset=utf-8" that looks like this:
<*image.RGBA64 Value>
I am pretty sure that I need to encode the image again and then serve it. But im not quite sure how to do this without saving the image to the disk...
Thanks in advance!
You can write to the ResponseWriter directly because it implements the io.Writer interface, no need to use a buffer or copy the image to disk.
You were almost there, just needed to set the content type and like you mentioned encode the image.Image object back into a jpeg. Luckily, the jpeg.Encode() method needed a writer to write to and you have the ResponseWriter available at your disposal to do just that thanks to Martini having the ability to inject it into your handler.
Note: you will probably want to do a more robust job of error handling than I have provided. This is just to get the ball rolling. ;)
package main
import (
"image"
"image/jpeg"
"log"
"net/http"
"os"
"github.com/codegangsta/martini"
"github.com/nfnt/resize"
)
func thumb() image.Image {
file, err := os.Open("test.jpg")
if err != nil {
log.Fatal(err)
}
img, err := jpeg.Decode(file)
if err != nil {
log.Fatal(err)
}
file.Close()
m := resize.Resize(0, 200, img, resize.MitchellNetravali)
return m
}
func main() {
m := martini.Classic()
m.Get("/", func(res http.ResponseWriter, req *http.Request) {
res.Header().Set("Content-Type", "image/jpeg")
err := jpeg.Encode(res, thumb(), &jpeg.Options{100})
if err != nil {
res.WriteHeader(500)
} else {
res.WriteHeader(200)
}
})
m.Run()
}

Resources