feat(upload): resumable chunked upload protocol for large files — init(parts≤32MB)/status/complete endpoints with fingerprint-derived deterministic uploadId (same file resumes, no restart), sessions in BOOKS_DIR/.uploads with 24h sweep, frontend uploadBook auto-switches >16MB to 8MB parts (retry x3 in place, skip received); oversize single POST now reports honest 413 too_large instead of "multipart field 'file' required" (was MaxBytesReader abort mislabeled); UPLOAD_MAX_MB wired into both compose stacks (cap total, .env.example 2048), nginx body limit drops to 32m; testCfg limit 1MB, CJK/fullwidth-colon filename proven unaffected; e2e verified with 311MB real file (39 parts, sha1 match)
This commit is contained in:
@@ -80,6 +80,41 @@ func TestLibraryCreateListUpload(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestUploadSizeAndFilename(t *testing.T) {
|
||||
_, _, h, booksDir := setupAPI(t)
|
||||
tok := adminToken(t, h)
|
||||
w := do(h, "POST", "/api/libraries", tok, map[string]string{"name": "s2"})
|
||||
if w.Code != 201 {
|
||||
t.Fatalf("create lib %d %s", w.Code, w.Body)
|
||||
}
|
||||
var lib map[string]any
|
||||
json.Unmarshal(w.Body.Bytes(), &lib)
|
||||
libID := itoa(lib["id"])
|
||||
// 全角冒号等非 ASCII 文件名正常落盘
|
||||
body, mw := uploadBody("調教開關:第二季.cbz", []byte("zipbytes"))
|
||||
req := httptest.NewRequest("POST", "/api/libraries/"+libID+"/upload", body)
|
||||
req.Header.Set("Content-Type", mw.FormDataContentType())
|
||||
req.Header.Set("Authorization", "Bearer "+tok)
|
||||
ww := httptest.NewRecorder()
|
||||
h.ServeHTTP(ww, req)
|
||||
if ww.Code != 202 {
|
||||
t.Fatalf("cjk name upload %d %s", ww.Code, ww.Body)
|
||||
}
|
||||
if _, err := os.Stat(filepath.Join(booksDir, "s2", "調教開關:第二季.cbz")); err != nil {
|
||||
t.Fatal("cjk upload missing:", err)
|
||||
}
|
||||
// 超过 UploadMaxMB(测试=1MB)→ 413 too_large,而非误报 "file required"
|
||||
body, mw = uploadBody("big.cbz", bytes.Repeat([]byte("x"), 2<<20))
|
||||
req = httptest.NewRequest("POST", "/api/libraries/"+libID+"/upload", body)
|
||||
req.Header.Set("Content-Type", mw.FormDataContentType())
|
||||
req.Header.Set("Authorization", "Bearer "+tok)
|
||||
ww = httptest.NewRecorder()
|
||||
h.ServeHTTP(ww, req)
|
||||
if ww.Code != 413 || !strings.Contains(ww.Body.String(), "too_large") {
|
||||
t.Fatalf("oversize want 413 too_large got %d %s", ww.Code, ww.Body)
|
||||
}
|
||||
}
|
||||
|
||||
func uploadBody(filename string, content []byte) (*bytes.Buffer, *multipart.Writer) {
|
||||
buf := &bytes.Buffer{}
|
||||
mw := multipart.NewWriter(buf)
|
||||
|
||||
Reference in New Issue
Block a user