Golang实现Web表单文件上传到指定URL的方法

Golang实现Web表单文件上传到指定URL的方法 使用 Go 和 http.NewRequest,将 Web 表单以 multipart 模式接收到的文件转发到任意 URL 的最佳方法是什么?

1 回复

更多关于Golang实现Web表单文件上传到指定URL的方法的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


在Go中实现将接收到的multipart表单文件转发到其他URL,可以通过以下方式处理:

package main

import (
    "bytes"
    "io"
    "mime/multipart"
    "net/http"
    "os"
)

// 处理上传并转发文件
func uploadAndForwardHandler(w http.ResponseWriter, r *http.Request) {
    // 解析multipart表单,限制大小为10MB
    err := r.ParseMultipartForm(10 << 20)
    if err != nil {
        http.Error(w, "无法解析表单", http.StatusBadRequest)
        return
    }

    // 获取文件
    file, header, err := r.FormFile("file")
    if err != nil {
        http.Error(w, "无法获取文件", http.StatusBadRequest)
        return
    }
    defer file.Close()

    // 创建缓冲区存储文件数据
    var buf bytes.Buffer
    writer := multipart.NewWriter(&buf)

    // 创建表单文件字段
    part, err := writer.CreateFormFile("file", header.Filename)
    if err != nil {
        http.Error(w, "创建表单字段失败", http.StatusInternalServerError)
        return
    }

    // 复制文件数据
    _, err = io.Copy(part, file)
    if err != nil {
        http.Error(w, "复制文件数据失败", http.StatusInternalServerError)
        return
    }

    // 添加其他表单字段(如果需要)
    _ = writer.WriteField("key", "value")

    // 关闭writer以完成multipart数据
    writer.Close()

    // 创建转发请求
    targetURL := "https://目标URL/上传端点"
    req, err := http.NewRequest("POST", targetURL, &buf)
    if err != nil {
        http.Error(w, "创建请求失败", http.StatusInternalServerError)
        return
    }

    // 设置正确的Content-Type头部
    req.Header.Set("Content-Type", writer.FormDataContentType())

    // 发送请求
    client := &http.Client{}
    resp, err := client.Do(req)
    if err != nil {
        http.Error(w, "转发请求失败", http.StatusInternalServerError)
        return
    }
    defer resp.Body.Close()

    // 读取响应并返回给客户端
    body, err := io.ReadAll(resp.Body)
    if err != nil {
        http.Error(w, "读取响应失败", http.StatusInternalServerError)
        return
    }

    w.Header().Set("Content-Type", resp.Header.Get("Content-Type"))
    w.WriteHeader(resp.StatusCode)
    w.Write(body)
}

// 流式传输版本,适用于大文件
func streamUploadHandler(w http.ResponseWriter, r *http.Request) {
    // 解析multipart表单
    reader, err := r.MultipartReader()
    if err != nil {
        http.Error(w, "无法创建multipart读取器", http.StatusBadRequest)
        return
    }

    // 创建管道用于流式传输
    pr, pw := io.Pipe()
    writer := multipart.NewWriter(pw)

    // 启动goroutine处理流式写入
    go func() {
        defer pw.Close()
        defer writer.Close()

        for {
            part, err := reader.NextPart()
            if err == io.EOF {
                break
            }
            if err != nil {
                pw.CloseWithError(err)
                return
            }

            // 创建对应的表单部分
            formPart, err := writer.CreatePart(part.Header)
            if err != nil {
                pw.CloseWithError(err)
                return
            }

            // 复制数据
            _, err = io.Copy(formPart, part)
            part.Close()
            if err != nil {
                pw.CloseWithError(err)
                return
            }
        }
    }()

    // 创建转发请求
    targetURL := "https://目标URL/上传端点"
    req, err := http.NewRequest("POST", targetURL, pr)
    if err != nil {
        http.Error(w, "创建请求失败", http.StatusInternalServerError)
        return
    }

    req.Header.Set("Content-Type", writer.FormDataContentType())

    // 发送请求
    client := &http.Client{}
    resp, err := client.Do(req)
    if err != nil {
        http.Error(w, "转发请求失败", http.StatusInternalServerError)
        return
    }
    defer resp.Body.Close()

    // 返回响应
    io.Copy(w, resp.Body)
}

func main() {
    http.HandleFunc("/upload", uploadAndForwardHandler)
    http.HandleFunc("/stream-upload", streamUploadHandler)
    http.ListenAndServe(":8080", nil)
}

对于更高效的内存使用,可以使用io.Pipe实现流式传输:

func forwardFileStream(sourceURL, targetURL string) error {
    // 从源URL获取文件
    resp, err := http.Get(sourceURL)
    if err != nil {
        return err
    }
    defer resp.Body.Close()

    // 创建管道
    pr, pw := io.Pipe()
    writer := multipart.NewWriter(pw)

    // 启动goroutine写入数据
    go func() {
        defer pw.Close()
        defer writer.Close()

        // 创建表单文件字段
        part, err := writer.CreateFormFile("file", "filename.ext")
        if err != nil {
            pw.CloseWithError(err)
            return
        }

        // 流式复制数据
        _, err = io.Copy(part, resp.Body)
        if err != nil {
            pw.CloseWithError(err)
            return
        }
    }()

    // 创建转发请求
    req, err := http.NewRequest("POST", targetURL, pr)
    if err != nil {
        return err
    }
    req.Header.Set("Content-Type", writer.FormDataContentType())

    // 发送请求
    client := &http.Client{}
    forwardResp, err := client.Do(req)
    if err != nil {
        return err
    }
    defer forwardResp.Body.Close()

    return nil
}

这些示例展示了如何使用http.NewRequestmultipart.Writer将接收到的文件转发到其他URL,第一个版本适合中小文件,第二个流式版本适合大文件传输。

回到顶部