flipped-aurora/gin-vue-admin · error

读取文件失败, err:

Error message

读取文件失败, err:

What it means

This error is returned by Minio.UploadFile when io.Copy(&filecontent, f) fails while reading the opened multipart file into a bytes.Buffer before uploading to MinIO. It indicates the upload stream broke mid-read — typically a client disconnect or truncated request body.

Source

Thrown at server/utils/upload/minio_oss.go:60

func (m *Minio) UploadFile(ctx context.Context, file *multipart.FileHeader) (filePathres, key string, uploadErr error) {
	client, err := newMinioClient()
	if err != nil {
		logger.WithCtx(ctx).Mod("upload").Err(err).Error("minio client 初始化失败")
		return "", "", errors.New("minio client 初始化失败, err:" + err.Error())
	}

	f, openError := file.Open()
	// mutipart.File to os.File
	if openError != nil {
		logger.WithCtx(ctx).Mod("upload").Err(openError).Error("function file.Open() Failed")
		return "", "", errors.New("function file.Open() Failed, err:" + openError.Error())
	}

	filecontent := bytes.Buffer{}
	_, err = io.Copy(&filecontent, f)
	if err != nil {
		logger.WithCtx(ctx).Mod("upload").Err(err).Error("读取文件失败")
		return "", "", errors.New("读取文件失败, err:" + err.Error())
	}
	f.Close() // 创建文件 defer 关闭

	// 对文件名进行加密存储
	ext := filepath.Ext(file.Filename)
	filename := utils.MD5V([]byte(strings.TrimSuffix(file.Filename, ext))) + ext
	if global.GVA_CONFIG.Minio.BasePath == "" {
		filePathres = "uploads" + "/" + time.Now().Format("2006-01-02") + "/" + filename
	} else {
		filePathres = global.GVA_CONFIG.Minio.BasePath + "/" + time.Now().Format("2006-01-02") + "/" + filename
	}

	// 根据文件扩展名检测 MIME 类型
	contentType := mime.TypeByExtension(ext)
	if contentType == "" {
		contentType = "application/octet-stream"
	}

View on GitHub (pinned to 3136500ef3)

Solutions

  1. Read the appended error: 'unexpected EOF'/'context canceled' means the client or a proxy dropped the connection — retry from the client and increase proxy timeouts.
  2. Increase reverse-proxy request/read timeouts (nginx proxy_read_timeout, LB idle timeout) to exceed the worst-case upload duration.
  3. Check and raise any body-size limits so large files aren't truncated mid-stream.
  4. For unreliable networks, implement client-side chunked/resumable upload logic rather than a single monolithic multipart POST.

Example fix

// before (nginx)
proxy_read_timeout 30s;   # large upload over slow link cut off mid-read
// after (nginx)
proxy_read_timeout 600s;
proxy_request_buffering off;
Defensive patterns

Strategy: retry

Validate before calling

if fileHeader == nil || fileHeader.Size <= 0 {
    return errors.New("empty upload")
}
// optional headroom/limit check to reject hopeless uploads early
if fileHeader.Size > maxUploadSize {
    return fmt.Errorf("upload too large: %d > %d", fileHeader.Size, maxUploadSize)
}

Try / catch

filePath, key, err := minioUploader.UploadFile(ctx, fileHeader)
if err != nil {
    if strings.HasPrefix(err.Error(), "读取文件失败") {
        // stream broke mid-read; typically transient — client should retry
        return status.NewRetryableError(err) // or map to 503 with Retry-After
    }
    return err
}

Prevention

When it happens

Trigger: UploadFile in progress and the client aborts the upload (browser close, network drop), a proxy terminates the connection due to timeouts, or the request body is truncated by size-limit middleware mid-transfer.

Common situations: Users cancelling large uploads; mobile clients on flaky connections; nginx/ALB idle timeouts shorter than the upload duration; request body size limits (e.g. http.MaxBytesReader) hit partway through streaming.

Related errors


AI-assisted analysis of flipped-aurora/gin-vue-admin@3136500ef3 (2026-08-31). Data as JSON: /api/errors/796f4df6e84d990a. Report an issue: GitHub.