尝试在内存中按需转换图像,并返回转换后的图像。

huangapple go评论86阅读模式
英文:

Trying to convert image on demand in memory and return it converted

问题

我正在尝试将图像即时转换为PNG格式,源图像可以是TIFF、JPEG或PNG格式,我希望始终将其作为PNG格式返回。

目前我在获取图像并进行HTTP响应时遇到了问题,所以非常感谢任何指点,因为当前从res.write返回的错误是"Cannot use 'destImage' (type error) as the type []byte"。

如果可能的话,最好在内存中进行图像处理,而不是在文件系统上进行。

以下是代码的翻译:

import (
	"bufio"
	"bytes"
	"crypto/tls"
	"log"
	"net/http"

	"github.com/sunshineplan/imgconv"
)

var client = http.Client{}

func main() {

	http.HandleFunc("/photo", cutterHandler)
	http.ListenAndServe(":8000", nil)
}

func cutterHandler(res http.ResponseWriter, req *http.Request) {
	url := "https://proxy.local/Image.aspx?id=CAB96C9C-DA56-4BC9-8626-FC5C95FF4D95"
	http.DefaultTransport.(*http.Transport).TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
	reqImg, err := client.Get(url)
	if err != nil {
		log.Println(res, "Error %d", err)
		req.Response.StatusCode = http.StatusInternalServerError
		return
	}

	srcImage, err := imgconv.Decode(reqImg.Body)
	if err != nil {
		log.Println(err)
		req.Response.StatusCode = http.StatusInternalServerError
		return
	}

	convertedImage := imgconv.Resize(srcImage, imgconv.ResizeOption{Width: 128, Height: 128})
	var b bytes.Buffer
	buffer := bufio.NewWriter(&b)
	destImage := imgconv.Write(buffer, convertedImage, imgconv.FormatOption{Format: imgconv.PNG})
	res.WriteHeader(http.StatusOK)
	res.Header().Set("Content-Type", "image/png")
	res.Write(destImage)
}
英文:

I'm trying to convert an image on the fly to PNG, the source can be TIFF, jpeg, or png and I want it to always be returned as png.

Currently I'm having issues with getting the image out in a http response, so any pointers would be highly appreciated as the current error from res.write is "Cannot use 'destImage' (type error) as the type []byte"

The image processing is preferred to be done in memory rather than on file system if at all possible.

import (
	"bufio"
	"bytes"
	"crypto/tls"
	"log"
	"net/http"

	"github.com/sunshineplan/imgconv"
)

var client = http.Client{}

func main() {

	http.HandleFunc("/photo", cutterHandler)
	http.ListenAndServe(":8000", nil)
}

func cutterHandler(res http.ResponseWriter, req *http.Request) {
	url := "https://proxy.local/Image.aspx?id=CAB96C9C-DA56-4BC9-8626-FC5C95FF4D95"
	http.DefaultTransport.(*http.Transport).TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
	reqImg, err := client.Get(url)
	if err != nil {
		log.Println(res, "Error %d", err)
		req.Response.StatusCode = http.StatusInternalServerError
		return
	}

	srcImage, err := imgconv.Decode(reqImg.Body)
	if err != nil {
		log.Println(err)
		req.Response.StatusCode = http.StatusInternalServerError
		return
	}

	convertedImage := imgconv.Resize(srcImage, imgconv.ResizeOption{Width: 128, Height: 128})
	var b bytes.Buffer
	buffer := bufio.NewWriter(&b)
	destImage := imgconv.Write(buffer, convertedImage, imgconv.FormatOption{Format: imgconv.PNG})
	res.WriteHeader(http.StatusOK)
	res.Header().Set("Content-Type", "image/png")
	res.Write(destImage)
}

答案1

得分: 2

问题是ResponseWriter.Write()方法接受一个字节切片([]byte),但你试图传递一个错误类型(built-in interface type)。

如果我尝试构建你的代码,我会得到以下错误信息:

>./main.go:44:15: cannot use destImage (variable of type error) as type []byte in argument to res.Write

在源代码中,我看到"destImage"变量在第41行被初始化,并且具有错误类型(imgconv.Write()方法返回一个错误)。

 destImage := imgconv.Write(buffer, convertedImage, imgconv.FormatOption{Format: imgconv.PNG})

要修复错误,你需要在第41行更改imgconv.Write()方法的调用。可以按以下方式完成:

convertedImage := imgconv.Resize(srcImage, imgconv.ResizeOption{Width: 128, Height: 128})

res.Header().Set("Content-Type", "image/png")
res.WriteHeader(http.StatusOK)

err = imgconv.Write(res, convertedImage, imgconv.FormatOption{Format: imgconv.PNG})
if err != nil {
    log.Println(err)
}

为了演示,我写了另一个版本,你可能会觉得有用。

package main

import (
	"bytes"
	"crypto/tls"
	"fmt"
	"io"
	"log"
	"net/http"
    "strconv"

	"github.com/sunshineplan/imgconv"
)

var client = http.Client{
	Transport: cloneTransport(),
}

func main() {
	imageURL := ImageURL("https://go.dev/blog/go-brand/Go-Logo/JPG/Go-Logo_Blue.jpg")
	http.Handle("/logos/golang.png", imageURL)
	log.Fatal(http.ListenAndServe(":8080", nil))
}

type ImageURL string

func (imageURL ImageURL) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	if req.Method != http.MethodGet {
		w.WriteHeader(http.StatusMethodNotAllowed)
		return
	}

	res, err := client.Get(string(imageURL))

	if err != nil {
		log.Printf("fetch %q image: %v", imageURL, err)
		w.WriteHeader(http.StatusInternalServerError)
		return
	}

	if res.StatusCode != http.StatusOK {
		log.Printf("fetch %q image: %s [%d]", imageURL, res.Status, res.StatusCode)
		w.WriteHeader(http.StatusInternalServerError)
		return
	}

	buffer := new(bytes.Buffer)
	err = convert(buffer, res.Body)

	defer res.Body.Close()

	if err != nil {
		log.Printf("convert %q image: %v", imageURL, err)
		w.WriteHeader(http.StatusInternalServerError)
		return
	}

	w.Header().Set("Content-Type", "image/png")
    w.Header().Set("Content-Length", strconv.FormatInt(int64(buffer.Len()), 10))
	w.WriteHeader(http.StatusOK)

	if _, err = buffer.WriteTo(w); err != nil {
		log.Printf("write data: %v", err)
	}
}

func convert(w io.Writer, r io.Reader) error {
	srcImage, err := imgconv.Decode(r)

	if err != nil {
		return fmt.Errorf("decode image: %w", err)
	}

	img := imgconv.Resize(srcImage, imgconv.ResizeOption{Width: 128, Height: 128})
	err = imgconv.Write(w, img, imgconv.FormatOption{Format: imgconv.PNG})

	if err != nil {
		return fmt.Errorf("encode image: %w", err)
	}

	return nil
}

func cloneTransport() *http.Transport {
	transport := http.DefaultTransport.(*http.Transport).Clone()
	transport.TLSClientConfig = &tls.Config{
		InsecureSkipVerify: true,
	}

	return transport
}

另外,还有一个没有CMYK支持(TIFF),使用/x/image/tiffresize包的convert()函数的另一个变体

import (
    "fmt"
    "image"
    "image/png"
    "io"

    _ "golang.org/x/image/tiff"
    _ "image/jpeg"

    "github.com/nfnt/resize"
)

func convert(w io.Writer, r io.Reader) error {
    img, _, err := image.Decode(r)

    if err != nil {
        return fmt.Errorf("decode image: %w", err)
    }

    resizedImage := resize.Resize(128, 128, img, resize.Bicubic)
    err = png.Encode(w, resizedImage)

    if err != nil {
        return fmt.Errorf("encode image: %w", err)
    }

    return nil
}
英文:

The problem is that ResponseWriter.Write() method accepts a slice of bytes ([]byte), but you are trying to pass an error type (built-in interface type).

If I try to build your code I get the following error message:

>./main.go:44:15: cannot use destImage (variable of type error) as type []byte in argument to res.Write

In the source code, I see that "destImage" variable is initialized on line 41 and has an error type (method imgconv.Write() returns an error).

 destImage := imgconv.Write(buffer, convertedImage, imgconv.FormatOption{Format: imgconv.PNG})

To fix the error you need to change imgconv.Write() method call at line 41. And it can be done in the following way:

convertedImage := imgconv.Resize(srcImage, imgconv.ResizeOption{Width: 128, Height: 128})
res.Header().Set("Content-Type", "image/png")
res.WriteHeader(http.StatusOK)
err = imgconv.Write(res, convertedImage, imgconv.FormatOption{Format: imgconv.PNG})
if err != nil {
log.Println(err)
}

To demonstrate, I wrote another version that you may find useful.

package main
import (
"bytes"
"crypto/tls"
"fmt"
"io"
"log"
"net/http"
"strconv"
"github.com/sunshineplan/imgconv"
)
var client = http.Client{
Transport: cloneTransport(),
}
func main() {
imageURL := ImageURL("https://go.dev/blog/go-brand/Go-Logo/JPG/Go-Logo_Blue.jpg")
http.Handle("/logos/golang.png", imageURL)
log.Fatal(http.ListenAndServe(":8080", nil))
}
type ImageURL string
func (imageURL ImageURL) ServeHTTP(w http.ResponseWriter, req *http.Request) {
if req.Method != http.MethodGet {
w.WriteHeader(http.StatusMethodNotAllowed)
return
}
res, err := client.Get(string(imageURL))
if err != nil {
log.Printf("fetch %q image: %v", imageURL, err)
w.WriteHeader(http.StatusInternalServerError)
return
}
if res.StatusCode != http.StatusOK {
log.Printf("fetch %q image: %s [%d]", imageURL, res.Status, res.StatusCode)
w.WriteHeader(http.StatusInternalServerError)
return
}
buffer := new(bytes.Buffer)
err = convert(buffer, res.Body)
defer res.Body.Close()
if err != nil {
log.Printf("convert %q image: %v", imageURL, err)
w.WriteHeader(http.StatusInternalServerError)
return
}
w.Header().Set("Content-Type", "image/png")
w.Header().Set("Content-Length", strconv.FormatInt(int64(buffer.Len()), 10))
w.WriteHeader(http.StatusOK)
if _, err = buffer.WriteTo(w); err != nil {
log.Printf("write data: %v", err)
}
}
func convert(w io.Writer, r io.Reader) error {
srcImage, err := imgconv.Decode(r)
if err != nil {
return fmt.Errorf("decode image: %w", err)
}
img := imgconv.Resize(srcImage, imgconv.ResizeOption{Width: 128, Height: 128})
err = imgconv.Write(w, img, imgconv.FormatOption{Format: imgconv.PNG})
if err != nil {
return fmt.Errorf("encode image: %w", err)
}
return nil
}
func cloneTransport() *http.Transport {
transport := http.DefaultTransport.(*http.Transport).Clone()
transport.TLSClientConfig = &tls.Config{
InsecureSkipVerify: true,
}
return transport
}

Also, another variant of convert() function without CMYK support (TIFF), with using /x/image/tiff and resize packages.

import (
"fmt"
"image"
"image/png"
"io"
_ "golang.org/x/image/tiff"
_ "image/jpeg"
"github.com/nfnt/resize"
)
func convert(w io.Writer, r io.Reader) error {
img, _, err := image.Decode(r)
if err != nil {
return fmt.Errorf("decode image: %w", err)
}
resizedImage := resize.Resize(128, 128, img, resize.Bicubic)
err = png.Encode(w, resizedImage)
if err != nil {
return fmt.Errorf("encode image: %w", err)
}
return nil
}

huangapple
  • 本文由 发表于 2022年5月18日 20:41:26
  • 转载请务必保留本文链接:https://go.coder-hub.com/72289400.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定