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

  1. Recompute the chunk MD5 client-side immediately before sending the exact bytes being transmitted
  2. Ensure the file is not modified between hashing and upload (close writers, retry from a stable snapshot)
  3. Verify the client uses the same byte range boundaries for hashing and for the body
  4. Retry the failed chunk upload once the hash is recomputed; the chunk is rejected atomically so no partial state is written
  5. 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

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.