flipped-aurora/gin-vue-admin · error
分片 %d 校验失败
Error message
分片 %d 校验失败
What it means
SaveChunk verifies each uploaded chunk's integrity by computing the MD5 of the received bytes and comparing it to the chunkHash supplied by the client. On mismatch it returns "分片 %d 校验失败" (chunk N verification failed) and does not persist the chunk. This protects the merged file from corrupted or mismatched pieces.
Source
Thrown at server/service/media/media_upload.go:86
resp.UploadID = up.ID
resp.UploadedChunks = idx
return resp, nil
}
func (s *MediaUploadService) SaveChunk(ctx context.Context, userID, uploadID uint, index int, chunkHash string, data []byte) error {
var up media.MediaUpload
if err := global.GVA_DB.WithContext(ctx).First(&up, uploadID).Error; err != nil {
return errors.New("上传会话不存在")
}
if up.UserID != userID {
return errors.New("无权操作该上传")
}
if up.Status != media.UploadStatusUploading {
return errors.New("上传会话状态不允许收片")
}
sum := md5.Sum(data)
if hex.EncodeToString(sum[:]) != chunkHash {
return fmt.Errorf("分片 %d 校验失败", index)
}
if _, err := upload.SaveChunkFile(uploadID, index, data); err != nil {
return err
}
rec := media.MediaUploadChunk{UploadID: uploadID, ChunkIndex: index, ChunkHash: chunkHash, Size: int64(len(data))}
return global.GVA_DB.WithContext(ctx).Clauses(clause.OnConflict{
Columns: []clause.Column{{Name: "upload_id"}, {Name: "chunk_index"}},
DoNothing: true,
}).Create(&rec).Error
}
func (s *MediaUploadService) Complete(ctx context.Context, userID, uploadID uint) (media.FileUploadAndDownload, error) {
var m media.FileUploadAndDownload
var up media.MediaUpload
if err := global.GVA_DB.WithContext(ctx).First(&up, uploadID).Error; err != nil {
return m, errors.New("上传会话不存在")
}
if up.UserID != userID {View on GitHub (pinned to 3136500ef3)
Solutions
- Recompute the chunk MD5 client-side immediately before sending the exact bytes being transmitted
- Ensure the file is not modified between hashing and upload (close writers, retry from a stable snapshot)
- Verify the client uses the same byte range boundaries for hashing and for the body
- Retry the failed chunk upload once the hash is recomputed; the chunk is rejected atomically so no partial state is written
- Check any proxy/middleware that may mutate the request body between client and server
Example fix
// before: hash computed at selection time, file edited afterwards
const hash = md5(await file.slice(start, end))
await fetch(url, { method: 'PUT', body: await file.slice(start, end) })
// after: read slice once, hash the same buffer you send
const buf = new Uint8Array(await file.slice(start, end).arrayBuffer())
const hash = md5(buf)
await fetch(url, { method: 'PUT', body: buf }) Defensive patterns
Strategy: retry
Validate before calling
// client: hash the exact buffer you will send
const buf = new Uint8Array(await file.slice(start, end).arrayBuffer())
if (md5(buf) !== expectedChunkHash) {
throw new Error('local chunk hash mismatch before upload')
} Try / catch
err := svc.SaveChunk(ctx, uploadID, index, data, chunkHash)
if err != nil && strings.Contains(err.Error(), "校验失败") {
// recompute hash client-side and re-upload that single chunk
return retryChunkUpload(uploadID, index, data)
} Prevention
- Hash and send the identical byte slice in one operation
- Never mutate the file between hashing and upload
- Retry failed chunks with a freshly computed hash before calling Complete
- Don't reuse an uploadID across different files
When it happens
Trigger: Client computes the hash over different bytes than it sends (pre-encryption vs post-encryption), client hashes the wrong offset/slice, chunk index/hash mismatch in the request, or bytes corrupted in transit without TLS or altered by a proxy rewriting the body.
Common situations: Frontend reading file.slice() with off-by-one boundaries while hashing the whole slice differently; retries sending a newer file version whose chunk content changed after the hash was computed; middleware transforming request bodies (compression/gzip handled twice); race where the same uploadID is used for two different files.
Related errors
AI-assisted analysis of flipped-aurora/gin-vue-admin@3136500ef3 (2026-08-31).
Data as JSON: /api/errors/38fd4d29e68dc201.
Report an issue: GitHub.