You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Golang Gin接口发送附件邮件失败:PDF加载及路径问题排查

问题描述

基于Golang Gin框架开发的求职申请REST API,功能是将用户提交的表单发送至指定邮箱。遇到以下问题:

  1. 用户提交数据后,数据库choosefile字段显示“[binary data]”
  2. 收件人打开邮件附件时提示:
Error
Failed to load PDF document.
  1. 调试时输出错误:
2023/03/13 17:28:46 open C:\fakepath\deneme.pdf: The system cannot find the path specified. exit status 1
错误原因分析
  1. 前端路径伪造:C:\fakepath\是浏览器为安全机制生成的虚假本地路径,服务器无法访问该路径,直接用这个路径读取文件必然失败。
  2. 文件接收方式错误:当前用BindJSON接收choosefile字段,但文件上传不能通过JSON格式传递,必须用multipart/form-data格式,通过Gin的文件上传API接收实际文件内容。
  3. 数据库存储逻辑错误:错误地将无效路径(或浏览器生成的二进制标识)存入数据库,而非实际文件内容或有效存储路径。
修复方案

1. 调整前端请求格式

前端需将表单提交方式改为multipart/form-data,文件输入控件的name属性设为choosefile。

2. 修正后端文件接收逻辑

替换原有的JSON绑定逻辑,改用Gin的FormFile接收上传文件:

router.POST("/post", func(c *gin.Context) {
    // 接收上传的文件
    file, header, err := c.FormFile("choosefile")
    if err != nil {
        c.JSON(http.StatusBadRequest, gin.H{"error": "文件上传失败"})
        return
    }
    defer file.Close()

    // 可选:将文件保存到服务器本地(推荐用于持久化)
    savePath := "./uploads/" + header.Filename
    if err := c.SaveUploadedFile(header, savePath); err != nil {
        c.JSON(http.StatusInternalServerError, gin.H{"error": "文件保存失败"})
        return
    }

3. 修正数据库存储逻辑

方案A:存储文件内容(适合小文件)

将数据库choosefile字段类型改为bytea(PostgreSQL)或BLOB(MySQL),然后读取文件内容存入:

// 读取文件内容
fileContent, err := io.ReadAll(file)
if err != nil {
    c.JSON(http.StatusInternalServerError, gin.H{"error": "读取文件内容失败"})
    return
}

// 插入数据库
if _, err := db.Exec("INSERT INTO tablo(choosefile) VALUES ($1)", fileContent); err != nil {
    c.JSON(http.StatusInternalServerError, gin.H{"error": "数据库错误"})
    return
}

方案B:存储文件路径(节省数据库空间,推荐)

// 插入数据库(存储服务器本地路径)
if _, err := db.Exec("INSERT INTO tablo(choosefile) VALUES ($1)", savePath); err != nil {
    c.JSON(http.StatusInternalServerError, gin.H{"error": "数据库错误"})
    return
}

4. 修复邮件附件发送逻辑

如果使用本地存储路径读取文件:

// 读取文件内容
fileContent, err := ioutil.ReadFile(savePath)
if err != nil {
    c.JSON(http.StatusInternalServerError, gin.H{"error": "读取附件文件失败"})
    return
}

// Base64编码文件内容
encodedContent := base64.StdEncoding.EncodeToString(fileContent)

// 组装邮件内容(修复MIME格式问题,补充文本内容)
auth := smtp.PlainAuth("", "xxx@gmail.com", "xxx", "smtp.gmail.com")
to := []string{"xxx@gmail.com"} 
msg := []byte("To: " + strings.Join(to, ",") + "\r\n" +
    "Subject: JobApply Form Submission\r\n" +
    "MIME-version: 1.0;\r\n" +
    "Content-Type: multipart/mixed; boundary=\"boundary123\"\r\n" +
    "\r\n" +
    "--boundary123\r\n" +
    "Content-Type: text/plain; charset=utf-8\r\n" +
    "\r\n" +
    "求职申请已提交,附件为简历文件\r\n" +
    "--boundary123\r\n" +
    "Content-Type: application/pdf\r\n" +
    "Content-Transfer-Encoding: base64\r\n" +
    "Content-Disposition: attachment; filename=\"" + header.Filename + "\"\r\n" +
    "\r\n" +
    encodedContent + "\r\n" +
    "--boundary123--")

if err := smtp.SendMail("smtp.gmail.com:587", auth, "example@gmail.com", to, msg); err != nil {
    c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to send email"})
    return
}

c.JSON(http.StatusOK, gin.H{"status": "success"})

5. 清理冗余代码

原代码中查询数据库并写入本地文件的逻辑(db.Query、os.Create相关部分)若无实际用途,可直接删除以简化代码。

内容的提问来源于stack exchange,提问作者Ozalp code

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.30 17:39:24