mirror of
https://github.com/gohugoio/hugo.git
synced 2024-05-11 05:54:58 +00:00
resource: Fix multi-threaded image processing issue
When doing something like this with the same image from a partial used in, say, both the home page and the single page:
```bash
{{ with $img }}
{{ $big := .Fill "1024x512 top" }}
{{ $small := $big.Resize "512x" }}
{{ end }}
```
There would be timing issues making Hugo in some cases try to process the same image with the same instructions in parallel.
You would experience errors of type:
```bash
png: invalid format: not enough pixel data
```
This commit works around that by adding a mutex per image. This should also improve the performance, sligthly, as it avoids duplicate work.
The current workaround before this fix is to always operate on the original:
```bash
{{ with $img }}
{{ $big := .Fill "1024x512 top" }}
{{ $small := .Fill "512x256 top" }}
{{ end }}
```
Fixes #4404
This commit is contained in:
@@ -6,8 +6,11 @@ import (
|
||||
|
||||
"image"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
"github.com/gohugoio/hugo/helpers"
|
||||
"github.com/gohugoio/hugo/hugofs"
|
||||
@@ -45,17 +48,53 @@ func newTestResourceSpecForBaseURL(assert *require.Assertions, baseURL string) *
|
||||
return spec
|
||||
}
|
||||
|
||||
func newTestResourceOsFs(assert *require.Assertions) *Spec {
|
||||
cfg := viper.New()
|
||||
cfg.Set("baseURL", "https://example.com")
|
||||
|
||||
workDir, err := ioutil.TempDir("", "hugores")
|
||||
|
||||
if runtime.GOOS == "darwin" && !strings.HasPrefix(workDir, "/private") {
|
||||
// To get the entry folder in line with the rest. This its a little bit
|
||||
// mysterious, but so be it.
|
||||
workDir = "/private" + workDir
|
||||
}
|
||||
|
||||
contentDir := "base"
|
||||
cfg.Set("workingDir", workDir)
|
||||
cfg.Set("contentDir", contentDir)
|
||||
cfg.Set("resourceDir", filepath.Join(workDir, "res"))
|
||||
|
||||
fs := hugofs.NewFrom(hugofs.Os, cfg)
|
||||
fs.Destination = &afero.MemMapFs{}
|
||||
|
||||
s, err := helpers.NewPathSpec(fs, cfg)
|
||||
|
||||
assert.NoError(err)
|
||||
|
||||
spec, err := NewSpec(s, media.DefaultTypes)
|
||||
assert.NoError(err)
|
||||
return spec
|
||||
|
||||
}
|
||||
|
||||
func fetchSunset(assert *require.Assertions) *Image {
|
||||
return fetchImage(assert, "sunset.jpg")
|
||||
}
|
||||
|
||||
func fetchImage(assert *require.Assertions, name string) *Image {
|
||||
spec := newTestResourceSpec(assert)
|
||||
return fetchImageForSpec(spec, assert, name)
|
||||
}
|
||||
|
||||
func fetchImageForSpec(spec *Spec, assert *require.Assertions, name string) *Image {
|
||||
src, err := os.Open("testdata/" + name)
|
||||
assert.NoError(err)
|
||||
|
||||
spec := newTestResourceSpec(assert)
|
||||
workingDir := spec.Cfg.GetString("workingDir")
|
||||
f := filepath.Join(workingDir, name)
|
||||
|
||||
out, err := spec.Fs.Source.Create("/b/" + name)
|
||||
out, err := spec.Fs.Source.Create(f)
|
||||
assert.NoError(err)
|
||||
_, err = io.Copy(out, src)
|
||||
out.Close()
|
||||
@@ -66,11 +105,10 @@ func fetchImage(assert *require.Assertions, name string) *Image {
|
||||
return path.Join("/a", s)
|
||||
}
|
||||
|
||||
r, err := spec.NewResourceFromFilename(factory, "/public", "/b/"+name, name)
|
||||
r, err := spec.NewResourceFromFilename(factory, "/public", f, name)
|
||||
assert.NoError(err)
|
||||
assert.IsType(&Image{}, r)
|
||||
return r.(*Image)
|
||||
|
||||
}
|
||||
|
||||
func assertFileCache(assert *require.Assertions, fs *hugofs.Fs, filename string, width, height int) {
|
||||
|
||||
Reference in New Issue
Block a user