Golang如何实现一次性上传多个文件

Golang如何实现一次性上传多个文件 我阅读了这篇博客和这个问题及其答案,但发现它们只讨论了单个文件。

    // 解析我们的多部分表单,10 << 20 指定了最大
    // 上传文件大小为 10 MB。
    r.ParseMultipartForm(10 << 20)
    // FormFile 返回给定键 `myFile` 的第一个文件
    // 它还返回 FileHeader,以便我们可以获取文件名、
    // 头部信息和文件大小
    file, handler, err := r.FormFile("myFile")
    if err != nil {
        fmt.Println("Error Retrieving the File")
        fmt.Println(err)
        return
    }
    defer file.Close()

我如何一次性上传多个文件?

如果我使用下面的 JavaScript 代码上传多个文件,如何在 GO 服务器端读取和处理它?

const formData = new FormData();
const photos = document.querySelector('input[type="file"][multiple]');

formData.append('title', 'My Vegas Vacation');
for (let i = 0; i < photos.files.length; i++) {
  formData.append('photos', photos.files[i]);
}

fetch('https://example.com/posts', {
  method: 'POST',
  body: formData,
})
.then(response => response.json())
.then(result => {
  console.log('Success:', result);
})
.catch(error => {
  console.error('Error:', error);
});

更多关于Golang如何实现一次性上传多个文件的实战教程也可以访问 https://www.itying.com/category-94-b0.html

2 回复

更多关于Golang如何实现一次性上传多个文件的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


在Go中处理多文件上传,可以使用r.MultipartFormr.FormFile的变体。以下是两种实现方式:

方法1:使用MultipartForm

func uploadHandler(w http.ResponseWriter, r *http.Request) {
    // 解析表单,限制上传大小
    err := r.ParseMultipartForm(32 << 20) // 32MB
    if err != nil {
        http.Error(w, err.Error(), http.StatusBadRequest)
        return
    }
    
    // 获取所有文件
    files := r.MultipartForm.File["photos"] // "photos"是前端字段名
    
    for i, fileHeader := range files {
        // 打开文件
        file, err := fileHeader.Open()
        if err != nil {
            http.Error(w, fmt.Sprintf("Error opening file %d: %v", i, err), http.StatusInternalServerError)
            return
        }
        defer file.Close()
        
        // 创建目标文件
        dst, err := os.Create(fileHeader.Filename)
        if err != nil {
            http.Error(w, fmt.Sprintf("Error creating file %d: %v", i, err), http.StatusInternalServerError)
            return
        }
        defer dst.Close()
        
        // 复制文件内容
        if _, err := io.Copy(dst, file); err != nil {
            http.Error(w, fmt.Sprintf("Error saving file %d: %v", i, err), http.StatusInternalServerError)
            return
        }
        
        fmt.Fprintf(w, "Uploaded file %d: %s\n", i, fileHeader.Filename)
    }
}

方法2:使用FormFile的变体

func uploadHandler(w http.ResponseWriter, r *http.Request) {
    // 解析表单
    err := r.ParseMultipartForm(32 << 20)
    if err != nil {
        http.Error(w, err.Error(), http.StatusBadRequest)
        return
    }
    
    // 获取表单
    form := r.MultipartForm
    
    // 遍历所有文件
    for fieldName, fileHeaders := range form.File {
        for _, fileHeader := range fileHeaders {
            file, err := fileHeader.Open()
            if err != nil {
                http.Error(w, err.Error(), http.StatusInternalServerError)
                return
            }
            defer file.Close()
            
            // 保存文件
            dst, err := os.Create("./uploads/" + fileHeader.Filename)
            if err != nil {
                http.Error(w, err.Error(), http.StatusInternalServerError)
                return
            }
            defer dst.Close()
            
            if _, err := io.Copy(dst, file); err != nil {
                http.Error(w, err.Error(), http.StatusInternalServerError)
                return
            }
            
            fmt.Fprintf(w, "Field: %s, File: %s uploaded\n", fieldName, fileHeader.Filename)
        }
    }
}

方法3:流式处理(适合大文件)

func uploadHandler(w http.ResponseWriter, r *http.Request) {
    // 创建解析器
    reader, err := r.MultipartReader()
    if err != nil {
        http.Error(w, err.Error(), http.StatusBadRequest)
        return
    }
    
    for {
        part, err := reader.NextPart()
        if err == io.EOF {
            break
        }
        if err != nil {
            http.Error(w, err.Error(), http.StatusInternalServerError)
            return
        }
        
        // 只处理文件部分
        if part.FileName() != "" {
            // 创建目标文件
            dst, err := os.Create("./uploads/" + part.FileName())
            if err != nil {
                http.Error(w, err.Error(), http.StatusInternalServerError)
                return
            }
            defer dst.Close()
            
            // 复制文件内容
            if _, err := io.Copy(dst, part); err != nil {
                http.Error(w, err.Error(), http.StatusInternalServerError)
                return
            }
            
            fmt.Fprintf(w, "Uploaded: %s\n", part.FileName())
        }
        
        part.Close()
    }
}

完整示例

package main

import (
    "fmt"
    "io"
    "net/http"
    "os"
    "path/filepath"
)

func uploadHandler(w http.ResponseWriter, r *http.Request) {
    if r.Method != "POST" {
        http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
        return
    }
    
    // 创建上传目录
    uploadDir := "./uploads"
    os.MkdirAll(uploadDir, 0755)
    
    // 解析表单
    err := r.ParseMultipartForm(32 << 20)
    if err != nil {
        http.Error(w, err.Error(), http.StatusBadRequest)
        return
    }
    
    // 获取所有文件
    files := r.MultipartForm.File["photos"]
    
    var uploadedFiles []string
    
    for _, fileHeader := range files {
        // 打开文件
        file, err := fileHeader.Open()
        if err != nil {
            http.Error(w, err.Error(), http.StatusInternalServerError)
            return
        }
        defer file.Close()
        
        // 创建安全文件名
        safeFileName := filepath.Base(fileHeader.Filename)
        filePath := filepath.Join(uploadDir, safeFileName)
        
        // 创建目标文件
        dst, err := os.Create(filePath)
        if err != nil {
            http.Error(w, err.Error(), http.StatusInternalServerError)
            return
        }
        defer dst.Close()
        
        // 复制文件内容
        if _, err := io.Copy(dst, file); err != nil {
            http.Error(w, err.Error(), http.StatusInternalServerError)
            return
        }
        
        uploadedFiles = append(uploadedFiles, safeFileName)
    }
    
    // 返回响应
    w.Header().Set("Content-Type", "application/json")
    fmt.Fprintf(w, `{"status": "success", "uploaded_files": %d, "files": %v}`, 
        len(uploadedFiles), uploadedFiles)
}

func main() {
    http.HandleFunc("/upload", uploadHandler)
    http.ListenAndServe(":8080", nil)
}

这些方法都能处理前端使用相同字段名(如"photos")上传的多个文件。r.MultipartForm.File["photos"]会返回一个[]*multipart.FileHeader切片,包含所有上传的文件。

回到顶部