优化网盘图片读取逻辑

优化网盘图片读取逻辑
This commit is contained in:
truewhile
2026-08-21 22:21:14 +08:00
parent cbe0d7e88f
commit 0bcb1fec87
4 changed files with 10 additions and 29 deletions
-1
View File
@@ -44,7 +44,6 @@ type ImageProxy struct {
const ( const (
imageBrowserCacheControl = "public, max-age=2592000, immutable" imageBrowserCacheControl = "public, max-age=2592000, immutable"
imagePlaceholderCacheControl = "no-store" imagePlaceholderCacheControl = "no-store"
imageNegativeCacheTTL = 6 * time.Hour
) )
// NewImageProxy is the constructor. // NewImageProxy is the constructor.
+2 -7
View File
@@ -132,14 +132,9 @@ func imageFileETag(key string, stat os.FileInfo) string {
} }
func freshNegativeImageCache(failPath string) bool { func freshNegativeImageCache(failPath string) bool {
stat, err := os.Stat(failPath) if _, err := os.Stat(failPath); err == nil {
if err != nil { _ = os.Remove(failPath)
return false
} }
if time.Since(stat.ModTime()) < imageNegativeCacheTTL {
return true
}
_ = os.Remove(failPath)
return false return false
} }
+4 -18
View File
@@ -86,9 +86,8 @@ func (p *ImageProxy) serveRemoteImage(ctx context.Context, w http.ResponseWriter
if !forceRefresh && serveCachedImageFile(w, r, key, cachePath) { if !forceRefresh && serveCachedImageFile(w, r, key, cachePath) {
return nil return nil
} }
if !forceRefresh && p.serveFreshRemoteFailure(w, failPath) { // No negative caching: a previously failed fetch is retried on every
return nil // subsequent request, so the image recovers as soon as upstream does.
}
data, ctype, contentLength, err := p.fetchAndCacheRemoteImage(ctx, raw, host, cachePath, failPath) data, ctype, contentLength, err := p.fetchAndCacheRemoteImage(ctx, raw, host, cachePath, failPath)
if err != nil { if err != nil {
if forceRefresh && serveCachedImageFile(w, r, key, cachePath) { if forceRefresh && serveCachedImageFile(w, r, key, cachePath) {
@@ -128,16 +127,6 @@ func (p *ImageProxy) removeUnusableImageCache(cachePath, failPath string) {
_ = os.Remove(failPath) _ = os.Remove(failPath)
} }
func (p *ImageProxy) serveFreshRemoteFailure(w http.ResponseWriter, failPath string) bool {
if stat, err := os.Stat(failPath); err == nil && time.Since(stat.ModTime()) < imageNegativeCacheTTL {
serveCachedPlaceholder(w)
return true
} else if err == nil {
_ = os.Remove(failPath)
}
return false
}
func (p *ImageProxy) fetchAndCacheRemoteImage(ctx context.Context, raw, host, cachePath, failPath string) ([]byte, string, string, error) { func (p *ImageProxy) fetchAndCacheRemoteImage(ctx context.Context, raw, host, cachePath, failPath string) ([]byte, string, string, error) {
if err := os.MkdirAll(p.cacheDir, 0o750); err != nil { if err := os.MkdirAll(p.cacheDir, 0o750); err != nil {
p.log.Warn("imageproxy: mkdir failed", zap.String("dir", p.cacheDir), zap.Error(err)) p.log.Warn("imageproxy: mkdir failed", zap.String("dir", p.cacheDir), zap.Error(err))
@@ -188,11 +177,8 @@ func (p *ImageProxy) Fetch(ctx context.Context, raw string) ([]byte, string, err
_ = os.Remove(cachePath) _ = os.Remove(cachePath)
_ = os.Remove(failPath) _ = os.Remove(failPath)
} }
if stat, err := os.Stat(failPath); err == nil && time.Since(stat.ModTime()) < imageNegativeCacheTTL { // No negative caching: a previously failed fetch is retried on every
return nil, "", errors.New("recent image fetch failure") // subsequent request, so the image recovers as soon as upstream does.
} else if err == nil {
_ = os.Remove(failPath)
}
data, ctype, _, err := p.fetchAndCacheRemoteImage(ctx, raw, host, cachePath, failPath) data, ctype, _, err := p.fetchAndCacheRemoteImage(ctx, raw, host, cachePath, failPath)
return data, ctype, err return data, ctype, err
} }
+4 -3
View File
@@ -17,7 +17,7 @@ import (
"github.com/ShukeBta/MediaStationGo/internal/config" "github.com/ShukeBta/MediaStationGo/internal/config"
) )
func TestImageProxyCachesFailedRemoteImageFetch(t *testing.T) { func TestImageProxyDoesNotNegativeCacheFailedRemoteImageFetch(t *testing.T) {
var calls int32 var calls int32
proxy := NewImageProxy(&config.Config{Cache: config.CacheConfig{CacheDir: filepath.Join(t.TempDir(), "cache")}}, zap.NewNop()) proxy := NewImageProxy(&config.Config{Cache: config.CacheConfig{CacheDir: filepath.Join(t.TempDir(), "cache")}}, zap.NewNop())
proxy.client = &http.Client{Transport: imageRoundTripFunc(func(req *http.Request) (*http.Response, error) { proxy.client = &http.Client{Transport: imageRoundTripFunc(func(req *http.Request) (*http.Response, error) {
@@ -42,12 +42,13 @@ func TestImageProxyCachesFailedRemoteImageFetch(t *testing.T) {
if rec.Body.Len() != len(transparent1x1PNG) { if rec.Body.Len() != len(transparent1x1PNG) {
t.Fatalf("body length = %d, want placeholder %d", rec.Body.Len(), len(transparent1x1PNG)) t.Fatalf("body length = %d, want placeholder %d", rec.Body.Len(), len(transparent1x1PNG))
} }
// No negative caching: every request re-attempts upstream.
if got := rec.Header().Get("Cache-Control"); got != imagePlaceholderCacheControl { if got := rec.Header().Get("Cache-Control"); got != imagePlaceholderCacheControl {
t.Fatalf("Cache-Control = %q, want %q", got, imagePlaceholderCacheControl) t.Fatalf("Cache-Control = %q, want %q", got, imagePlaceholderCacheControl)
} }
} }
if got := atomic.LoadInt32(&calls); got != 1 { if got := atomic.LoadInt32(&calls); got != 2 {
t.Fatalf("upstream calls = %d, want 1 due to negative cache", got) t.Fatalf("upstream calls = %d, want 2 (one per request, no negative cache)", got)
} }
} }