Gin 框架中的文件上传和下载实现方法如下:
1. 文件上传
1.1 单文件上传
gofunc uploadFile(c *gin.Context) { file, err := c.FormFile("file") if err != nil { c.JSON(400, gin.H{"error": err.Error()}) return } // 保存文件 dst := "./uploads/" + file.Filename if err := c.SaveUploadedFile(file, dst); err != nil { c.JSON(500, gin.H{"error": err.Error()}) return } c.JSON(200, gin.H{ "message": "File uploaded successfully", "filename": file.Filename, "size": file.Size, }) }
1.2 多文件上传
gofunc uploadMultipleFiles(c *gin.Context) { form, err := c.MultipartForm() if err != nil { c.JSON(400, gin.H{"error": err.Error()}) return } files := form.File["files"] var uploadedFiles []string for _, file := range files { dst := "./uploads/" + file.Filename if err := c.SaveUploadedFile(file, dst); err != nil { c.JSON(500, gin.H{"error": err.Error()}) return } uploadedFiles = append(uploadedFiles, file.Filename) } c.JSON(200, gin.H{ "message": "Files uploaded successfully", "files": uploadedFiles, }) }
1.3 文件大小限制
gofunc uploadFileWithLimit(c *gin.Context) { // 限制文件大小为 10MB c.Request.Body = http.MaxBytesReader(c.Writer, c.Request.Body, 10<<20) file, err := c.FormFile("file") if err != nil { c.JSON(400, gin.H{"error": "File too large"}) return } // 处理文件上传... }
1.4 文件类型验证
gofunc uploadFileWithValidation(c *gin.Context) { file, err := c.FormFile("file") if err != nil { c.JSON(400, gin.H{"error": err.Error()}) return } // 验证文件类型 allowedTypes := []string{"image/jpeg", "image/png", "image/gif"} fileHeader, err := file.Open() if err != nil { c.JSON(500, gin.H{"error": err.Error()}) return } defer fileHeader.Close() buffer := make([]byte, 512) _, err = fileHeader.Read(buffer) if err != nil { c.JSON(500, gin.H{"error": err.Error()}) return } contentType := http.DetectContentType(buffer) isAllowed := false for _, allowedType := range allowedTypes { if contentType == allowedType { isAllowed = true break } } if !isAllowed { c.JSON(400, gin.H{"error": "Invalid file type"}) return } // 处理文件上传... }
2. 文件下载
2.1 简单文件下载
gofunc downloadFile(c *gin.Context) { filename := c.Param("filename") filepath := "./uploads/" + filename c.File(filepath) }
2.2 带自定义文件名的下载
gofunc downloadFileWithCustomName(c *gin.Context) { filename := c.Param("filename") filepath := "./uploads/" + filename c.FileAttachment(filepath, "custom-name.pdf") }
2.3 流式下载
gofunc downloadFileStream(c *gin.Context) { filename := c.Param("filename") filepath := "./uploads/" + filename file, err := os.Open(filepath) if err != nil { c.JSON(404, gin.H{"error": "File not found"}) return } defer file.Close() fileInfo, _ := file.Stat() c.Header("Content-Disposition", "attachment; filename="+filename) c.Header("Content-Type", "application/octet-stream") c.Header("Content-Length", strconv.FormatInt(fileInfo.Size(), 10)) http.ServeContent(c.Writer, c.Request, filename, fileInfo.ModTime(), file) }
2.4 断点续传下载
gofunc downloadFileWithResume(c *gin.Context) { filename := c.Param("filename") filepath := "./uploads/" + filename file, err := os.Open(filepath) if err != nil { c.JSON(404, gin.H{"error": "File not found"}) return } defer file.Close() fileInfo, _ := file.Stat() // 处理 Range 请求 rangeHeader := c.GetHeader("Range") if rangeHeader != "" { // 解析 Range 头 ranges := strings.Split(rangeHeader, "=")[1] parts := strings.Split(ranges, "-") start, _ := strconv.ParseInt(parts[0], 10, 64) file.Seek(start, 0) remaining := fileInfo.Size() - start c.Header("Content-Range", fmt.Sprintf("bytes %d-%d/%d", start, fileInfo.Size()-1, fileInfo.Size())) c.Header("Content-Length", strconv.FormatInt(remaining, 10)) c.Header("Accept-Ranges", "bytes") c.Status(206) // Partial Content io.CopyN(c.Writer, file, remaining) return } c.Header("Accept-Ranges", "bytes") http.ServeContent(c.Writer, c.Request, filename, fileInfo.ModTime(), file) }
3. 最佳实践
3.1 文件存储
- 使用专门的存储服务(如 OSS、S3)
- 文件名使用 UUID 或时间戳避免冲突
- 按日期或用户分目录存储
3.2 安全考虑
- 验证文件类型和大小
- 扫描上传的文件(病毒检测)
- 限制上传目录的访问权限
- 对敏感文件进行加密
3.3 性能优化
- 使用流式处理大文件
- 实现断点续传
- 使用 CDN 加速文件下载
- 启用 gzip 压缩
3.4 错误处理
- 提供清晰的错误信息
- 记录上传下载日志
- 实现重试机制
通过以上方法,可以实现安全、高效的文件上传和下载功能。