英文:
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/tiff和resize包的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
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论