I'm creating an image dynamically when one doesn't exist. IE example_t500.jpg when requested would be created from example.jpg. The problem I'm having is displaying the created image when it's requested before a missing image is shown.
Code:
package main
import (
"image/jpeg"
"net/http"
"log"
"os"
"strings"
"fmt"
"strconv"
resizer "github.com/nfnt/resize"
)
func WebHandler (w http.ResponseWriter, r *http.Request) {
var Path = "../../static/img/photos/2014/11/4/test.jpg"
ResizeImage(Path, 500)
http.Handle("/", http.FileServer(http.Dir("example_t500.jpg")))
}
func ResizeImage (Path string, Width uint) {
var ImageExtension = strings.Split(Path, ".jpg")
var ImageNum = strings.Split(ImageExtension[0], "/")
var ImageName = ImageNum[len(ImageNum)-1]
fmt.Println(ImageName)
file, err := os.Open(Path)
if err != nil {
log.Fatal(err)
}
img, err := jpeg.Decode(file)
if err != nil {
log.Fatal(err)
}
file.Close()
m := resizer.Resize(Width, 0, img, resizer.Lanczos3)
out, err := os.Create(ImageName + "_t" + strconv.Itoa(int(Width)) + ".jpg")
if err != nil {
log.Fatal(err)
}
defer out.Close()
jpeg.Encode(out, m, nil)
}
func main() {
http.HandleFunc("/", WebHandler)
http.ListenAndServe(":8080", nil)
}
This is my first time trying to use Go and am having trouble rendering the image. Any help is appreciated.
There are a few things you need to do.
First, you need to remove this line from your WebHandler function:
http.Handle("/", http.FileServer(http.Dir("example_t500.jpg")))
That's setting up the default serve mux to handle the root route - but you've already done that in your main function here:
http.HandleFunc("/", WebHandler)
So every time you hit the root route, you're effectively just telling the servemux to handle it again .. but differently.
What you want to do.. is set the Content-Type header of the response.. then copy the contents of the file to the response stream. Something like this:
func WebHandler (w http.ResponseWriter, r *http.Request) {
var Path = "../../static/img/photos/2014/11/4/test.jpg"
ResizeImage(Path, 500)
img, err := os.Open("example_t500.jpg")
if err != nil {
log.Fatal(err) // perhaps handle this nicer
}
defer img.Close()
w.Header().Set("Content-Type", "image/jpeg") // <-- set the content-type header
io.Copy(w, img)
}
Thats the manual version. There's also http.ServeFile.
EDIT:
In response to your comment - if you don't want to write the file at all and just serve it dynamically, then all you need to do is pass the http.ResponseWriter to the Encode method. jpeg.Encode takes an io.Writer .. just as io.Copy does in my original example:
// Pass in the ResponseWriter
func ResizeImage (w io.Writer, Path string, Width uint) {
var ImageExtension = strings.Split(Path, ".jpg")
var ImageNum = strings.Split(ImageExtension[0], "/")
var ImageName = ImageNum[len(ImageNum)-1]
fmt.Println(ImageName)
file, err := os.Open(Path)
if err != nil {
log.Fatal(err)
}
img, err := jpeg.Decode(file)
if err != nil {
log.Fatal(err)
}
file.Close()
m := resizer.Resize(Width, 0, img, resizer.Lanczos3)
// Don't write the file..
/*out, err := os.Create(ImageName + "_t" + strconv.Itoa(int(Width)) + ".jpg")
if err != nil {
log.Fatal(err)
}
defer out.Close()*/
jpeg.Encode(w, m, nil) // Write to the ResponseWriter
}
Accept an io.Writer in the method .. then encode it to that. You can then call your method like this:
func WebHandler (w http.ResponseWriter, r *http.Request) {
var Path = "../../static/img/photos/2014/11/4/test.jpg"
ResizeImage(w, Path, 500) // pass the ResponseWriter in
}
The function you are looking for is http.ServeFile. Use that instead of http.Handle in your code.
Related
I'm trying to develop a Terraform provider but I have a problem of the first request body. Here is the code:
type Body struct {
id string
}
func resourceServerCreate(d *schema.ResourceData, m interface{}) error {
key := d.Get("key").(string)
token := d.Get("token").(string)
workspace_name := d.Get("workspace_name").(string)
board_name := d.Get("board_name").(string)
resp, err := http.Post("https://api.trello.com/1/organizations?key="+key+"&token="+token+"&displayName="+workspace_name,"application/json",nil)
if err != nil {
log.Fatalln(err)
}
defer resp.Body.Close()
//lettura body.
body := new(Body)
json.NewDecoder(resp.Body).Decode(body)
log.Println("[ORCA MADONNA] il log funzia "+body.id)
d.Set("board_id",body.id)
resp1, err1 := http.Post("https://api.trello.com/1/boards?key="+key+"&token="+token+"&idOrganization="+body.id+"&=&name="+board_name,"application/json",nil)
if err1 != nil {
log.Fatalln(resp1)
}
defer resp1.Body.Close()
d.SetId(board_name)
return resourceServerRead(d, m)
}
In the log is empty, but the second call have it and work fine. How is it possible?
Go doesn't force you to check error responses, therefore it's easy to make silly mistakes. Had you checked the return value from Decode(), you would have immediately discovered a problem.
err := json.NewDecoder(resp.Body).Decode(body)
if err != nil {
log.Fatal("Decode error: ", err)
}
Decode error: json: Unmarshal(non-pointer main.Body)
So your most immediate fix is to use & to pass a pointer to Decode():
json.NewDecoder(resp.Body).Decode(&body)
Also of note, some programming editors will highlight this mistake for you:
Here's a working demonstration, including a corrected Body structure as described at json.Marshal(struct) returns “{}”:
package main
import (
"bytes"
"encoding/json"
"fmt"
"log"
"net/http"
"time"
)
type JSON = map[string]interface{}
type JSONArray = []interface{}
func ErrFatal(err error, msg string) {
if err != nil {
log.Fatal(msg+": ", err)
}
}
func handleTestRequest(w http.ResponseWriter, req *http.Request) {
w.Write(([]byte)("{\"id\":\"yourid\"}"))
}
func launchTestServer() {
http.HandleFunc("/", handleTestRequest)
go http.ListenAndServe(":8080", nil)
time.Sleep(1 * time.Second) // allow server to get started
}
// Medium: "Don’t use Go’s default HTTP client (in production)"
var restClient = &http.Client{
Timeout: time.Second * 10,
}
func DoREST(method, url string, headers, payload JSON) *http.Response {
requestPayload, err := json.Marshal(payload)
ErrFatal(err, "json.Marshal(payload")
request, err := http.NewRequest(method, url, bytes.NewBuffer(requestPayload))
ErrFatal(err, "NewRequest "+method+" "+url)
for k, v := range headers {
request.Header.Add(k, v.(string))
}
response, err := restClient.Do(request)
ErrFatal(err, "DoRest client.Do")
return response
}
type Body struct {
Id string `json:"id"`
}
func clientDemo() {
response := DoREST("POST", "http://localhost:8080", JSON{}, JSON{})
defer response.Body.Close()
var body Body
err := json.NewDecoder(response.Body).Decode(&body)
ErrFatal(err, "Decode")
fmt.Printf("Body: %#v\n", body)
}
func main() {
launchTestServer()
for i := 0; i < 5; i++ {
clientDemo()
}
}
func (sticky *Sticky) DrawImage(W, H int) (img *image, err error) {
myImage := image.NewRGBA(image.Rect(0, 0, 10, 25))
myImage.Pix[0] = 55 // 1st pixel red
myImage.Pix[1] = 155 // 1st pixel green
return myImage ,nil
}
I am creating an image. I want to read the existing Image and return in this Function. How I can I do that?
Something like this:
func getImageFromFilePath(filePath string) (image.Image, error) {
f, err := os.Open(filePath)
if err != nil {
return nil, err
}
defer f.Close()
image, _, err := image.Decode(f)
return image, err
}
references
https://golang.org/pkg/image/#Decode
https://golang.org/pkg/os/#Open
try this:
package main
import (
"fmt"
"image"
"image/png"
"os"
)
func main() {
// Read image from file that already exists
existingImageFile, err := os.Open("test.png")
if err != nil {
// Handle error
}
defer existingImageFile.Close()
// Calling the generic image.Decode() will tell give us the data
// and type of image it is as a string. We expect "png"
imageData, imageType, err := image.Decode(existingImageFile)
if err != nil {
// Handle error
}
fmt.Println(imageData)
fmt.Println(imageType)
// We only need this because we already read from the file
// We have to reset the file pointer back to beginning
existingImageFile.Seek(0, 0)
// Alternatively, since we know it is a png already
// we can call png.Decode() directly
loadedImage, err := png.Decode(existingImageFile)
if err != nil {
// Handle error
}
fmt.Println(loadedImage)
}
references
https://www.devdungeon.com/content/working-images-go
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)
}
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()
}
I am trying to dynamically parse files using walk in a folder and I want to be able to set the path of the file "path/file.html". But my issue is if I have a file in a folder "path/folder/files.html" I can't do it because when I ExecuteTemplate the file name will be the same "files.html". Is it possible to name each template as I ParseFiles?
Im ok with doing a file one at a time if trying to do them all at once wont work.
// Parse file and send to responsewriter
func View(w http.ResponseWriter, path string) {
temp, err := template.ParseFiles("application/views/"+path+".html")
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
} else {
temp.ExecuteTemplate(w, path, nil)
}
}
Walk the filesystem using filepath.Walk and a consumer method that will create templates with the full file paths as names:
package main
import (
"fmt"
"html/template"
"os"
"path/filepath"
)
func consumer(p string, i os.FileInfo, e error) error {
t := template.New(p)
fmt.Println(t.Name())
return nil
}
func main() {
filepath.Walk("/path/to/template/root", filepath.WalkFunc(consumer))
}
You can try template.Lookup, the whole process looks like:
var (
templates *template.Template
)
func loadTemplate() {
funcMap := template.FuncMap{
"safe":func(s string) template.HTML {
return template.HTML(s)
},
}
var err error
templates, err = utils.BuildTemplate("/theme/path/", funcMap)
if err != nil {
log.Printf("Can't read template file %v,", err)
}
}
func homeHandler(w http.ResponseWriter, r *http.Request) {
//lookup the theme your want to use
templ = templates.Lookup("theme.html")
err := templ.Execute(w, data)
if err != nil {
log.Println(err)
}
}
func main() {
loadTemplate()
}
BuildTemplate looks like:
func BuildTemplate(dir string, funcMap template.FuncMap) (*template.Template, error) {
fs, err := ioutil.ReadDir(dir)
if err != nil {
fmt.Printf("Can't read template folder: %s\n", dir)
return nil, err
}
files := make([]string, len(fs))
for i, f := range (fs) {
files[i] = path.Join(dir, f.Name())
}
return template.Must(template.New("Template").Funcs(funcMap).ParseFiles(files...)), nil
}