go语言实现文件夹上传前后端代码案例

前端用于上传的测试界面

  • 如果上传的文件夹有子文件要遍历子文件夹创建出子文件夹再进行拷贝
  • 需要获取文件名和对应的路径,将文件的相对路径和文件对象添加到FormData中
  • 这几行代码很关键

 for (let i = 0; i < files.length; i++) {
            formData.append('model_folder', files[i], files[i].webkitRelativePath);
        }

前端代码:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Upload Folder Example</title>
</head>
<body>
<input type="file" id="folderInput" webkitdirectory mozdirectory directory multiple>
<button onclick="uploadFolder()">Upload</button>

<script>
    function uploadFolder() {
        let input = document.getElementById('folderInput');

        // 确保用户选择了一个文件夹
        if (!input.files || !input.files.length) {
            return alert('Please select a folder to upload.');
        }

        let files = input.files;
        let formData = new FormData();

        // 将文件的相对路径和文件对象添加到FormData中
        for (let i = 0; i < files.length; i++) {
            formData.append('model_folder', files[i], files[i].webkitRelativePath);
        }

        // 创建一个 XMLHttpRequest 对象进行异步请求
        let request = new XMLHttpRequest();
        request.open('POST', 'http://localhost:8089/digitalPerson/modeFile', true);

        request.onload = function() {
            if (request.status === 200) {
                // 文件上传成功的处理
                console.log(request.responseText);
            } else {
                // 文件上传失败的处理
                console.error(request.responseText);
            }
        };

        // 发送FormData对象到服务器
        request.send(formData);
    }
</script>
</body>
</html>

后端使用gin实现文件夹的上传和保存到对应的路径

package controllers

import (
	"PsycheEpic/src/utils"
	"errors"
	"fmt"
	"github.com/gin-gonic/gin"
	"net/http"
	"os"
	"path/filepath"
	"strings"
)

// 处理文件夹上传
func UploadFolderHandler(c *gin.Context, digitalId int64) error {
	err := c.Request.ParseMultipartForm(0) // 不限制上传文件大小
	if err != nil {
		c.JSON(http.StatusOK, gin.H{
			"code":    0,
			"message": err.Error(),
		})
		return errors.New("获取上传的文件失败")
	}
	//digitalId := 3
	// 数字人ID转换为字符串
	digital_Id := utils.Strval(digitalId)
	modePath := filepath.Join("./static/HuaSoul/asset/", digital_Id)
	fmt.Println("digitalId: ", digitalId)
	// 检查上传的文件是否存在
	form, err := c.MultipartForm()
	files := form.File["model_folder"] // 'files' 是前端 JavaScript 中指定的字段名
	if err != nil {
		c.JSON(http.StatusOK, gin.H{
			"code":    0,
			"message": "missing uploaded file"})
		return errors.New("丢失上传的文件")
	}
	// 创建模型文件夹
	if err := os.MkdirAll(modePath, 0755); err != nil {
		c.JSON(http.StatusOK, gin.H{
			"code":    0,
			"message": "failed to create model folder",
		})
		return errors.New("failed to create model folder")
	}

	// 遍历上传的文件
	for _, file := range files {
		fileName := file.Header["Content-Disposition"]
		path, _ := GetFileName(fileName)
		fmt.Println("path: ", path)
		savePath := filepath.Join(modePath, path) // 保存文件的路径,确保'uploads'文件夹已存在或自动创建
		if err := c.SaveUploadedFile(file, savePath); err != nil {
			c.String(http.StatusOK, fmt.Sprintf("'%s' could not be saved: %v", file.Filename, err))
			return err
		}
	}
	// 上传成功后返回响应
	//c.String(http.StatusOK, fmt.Sprintf("%d files uploaded!", len(files)))
	return nil
}

//fileName [form-data; name="model_folder"; filename="test/头像.jpg"] ;取出文件名
func GetFileName(fileName []string) (string, error) {
	for _, f := range fileName {
		// 判断字符串是否包含了需要查找的文件名关键字
		if strings.Contains(f, `filename=`) {
			// 按照 filename=" 分割
			parts := strings.Split(f, `filename="`)
			if len(parts) < 2 {
				// 没有找到分隔符,跳到下一个元素
				continue
			}
			// 按照 " 分割以获取实际的文件路径
			filePathParts := strings.SplitN(parts[1], `"`, 2)
			// 如果成功找到路径就返回
			if len(filePathParts) >= 2 {
				return filePathParts[0], nil // 返回找到的路径
			}
		}
	}
	// 如果没有找到,返回错误
	return "", fmt.Errorf("no path found")
}

12-19 13:39