imgbundler: Improve mime type detection
The order of priority in detecting the mime type is now: - Content-Type response header - File path extension - http.DetectContentType
This commit is contained in:
parent
eb1868cd0d
commit
b8b269db0c
1 changed files with 40 additions and 9 deletions
|
|
@ -6,9 +6,11 @@ import (
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
"mime"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
|
"path"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
@ -38,7 +40,11 @@ type repl struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func bundle(ctx context.Context, ms *xmain.State, svg []byte, isRemote bool) (_ []byte, err error) {
|
func bundle(ctx context.Context, ms *xmain.State, svg []byte, isRemote bool) (_ []byte, err error) {
|
||||||
defer xdefer.Errorf(&err, "failed to bundle images")
|
if isRemote {
|
||||||
|
defer xdefer.Errorf(&err, "failed to bundle remote images")
|
||||||
|
} else {
|
||||||
|
defer xdefer.Errorf(&err, "failed to bundle local images")
|
||||||
|
}
|
||||||
imgs := imageRegex.FindAllSubmatch(svg, -1)
|
imgs := imageRegex.FindAllSubmatch(svg, -1)
|
||||||
imgs = filterImageElements(imgs, isRemote)
|
imgs = filterImageElements(imgs, isRemote)
|
||||||
|
|
||||||
|
|
@ -65,9 +71,10 @@ func bundle(ctx context.Context, ms *xmain.State, svg []byte, isRemote bool) (_
|
||||||
}()
|
}()
|
||||||
|
|
||||||
var buf []byte
|
var buf []byte
|
||||||
|
var mimeType string
|
||||||
var err error
|
var err error
|
||||||
if isRemote {
|
if isRemote {
|
||||||
buf, err = httpGet(ctx, string(href))
|
buf, mimeType, err = httpGet(ctx, string(href))
|
||||||
} else {
|
} else {
|
||||||
buf, err = os.ReadFile(string(href))
|
buf, err = os.ReadFile(string(href))
|
||||||
}
|
}
|
||||||
|
|
@ -79,7 +86,9 @@ func bundle(ctx context.Context, ms *xmain.State, svg []byte, isRemote bool) (_
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
mimeType := http.DetectContentType(buf)
|
if mimeType == "" {
|
||||||
|
mimeType = sniffMimeType(href, buf, isRemote)
|
||||||
|
}
|
||||||
mimeType = strings.Replace(mimeType, "text/xml", "image/svg+xml", 1)
|
mimeType = strings.Replace(mimeType, "text/xml", "image/svg+xml", 1)
|
||||||
b64 := base64.StdEncoding.EncodeToString(buf)
|
b64 := base64.StdEncoding.EncodeToString(buf)
|
||||||
|
|
||||||
|
|
@ -108,7 +117,7 @@ func bundle(ctx context.Context, ms *xmain.State, svg []byte, isRemote bool) (_
|
||||||
case repl, ok := <-replc:
|
case repl, ok := <-replc:
|
||||||
if !ok {
|
if !ok {
|
||||||
if len(errhrefs) > 0 {
|
if len(errhrefs) > 0 {
|
||||||
return svg, xerrors.Errorf("failed to bundle the following images: %v", errhrefs)
|
return svg, xerrors.Errorf("%v", errhrefs)
|
||||||
}
|
}
|
||||||
return svg, nil
|
return svg, nil
|
||||||
}
|
}
|
||||||
|
|
@ -141,23 +150,45 @@ func filterImageElements(imgs [][][]byte, isRemote bool) [][][]byte {
|
||||||
|
|
||||||
var httpClient = &http.Client{}
|
var httpClient = &http.Client{}
|
||||||
|
|
||||||
func httpGet(ctx context.Context, href string) ([]byte, error) {
|
func httpGet(ctx context.Context, href string) ([]byte, string, error) {
|
||||||
ctx, cancel := context.WithTimeout(ctx, time.Minute)
|
ctx, cancel := context.WithTimeout(ctx, time.Minute)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
req, err := http.NewRequestWithContext(ctx, "GET", href, nil)
|
req, err := http.NewRequestWithContext(ctx, "GET", href, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
resp, err := httpClient.Do(req)
|
resp, err := httpClient.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, "", err
|
||||||
}
|
}
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
if resp.StatusCode != 200 {
|
if resp.StatusCode != 200 {
|
||||||
return nil, fmt.Errorf("expected status 200 but got %d %s", resp.StatusCode, resp.Status)
|
return nil, "", fmt.Errorf("expected status 200 but got %d %s", resp.StatusCode, resp.Status)
|
||||||
}
|
}
|
||||||
r := http.MaxBytesReader(nil, resp.Body, maxImageSize)
|
r := http.MaxBytesReader(nil, resp.Body, maxImageSize)
|
||||||
return ioutil.ReadAll(r)
|
buf, err := ioutil.ReadAll(r)
|
||||||
|
if err != nil {
|
||||||
|
return nil, "", err
|
||||||
|
}
|
||||||
|
return buf, resp.Header.Get("Content-Type"), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// sniffMimeType sniffs the mime type of href based on its file extension and contents.
|
||||||
|
func sniffMimeType(href, buf []byte, isRemote bool) string {
|
||||||
|
p := string(href)
|
||||||
|
if isRemote {
|
||||||
|
u, err := url.Parse(p)
|
||||||
|
if err != nil {
|
||||||
|
p = ""
|
||||||
|
} else {
|
||||||
|
p = u.Path
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mimeType := mime.TypeByExtension(path.Ext(p))
|
||||||
|
if mimeType == "" {
|
||||||
|
mimeType = http.DetectContentType(buf)
|
||||||
|
}
|
||||||
|
return mimeType
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue