javascript - 使用 go 处理文件上传

标签 javascript html http go multipartform-data

我最近才开始玩围棋,所以我还是菜鸟,如果我犯了太多错误,请原谅。很长一段时间以来,我一直试图解决这个问题,但我只是不明白发生了什么。在我的 main.go 文件中,我有一个主要功能:

func main() {
    http.HandleFunc("/", handler)
    http.HandleFunc("/submit/", submit)
    log.Fatal(http.ListenAndServe(":8080", nil))
}

处理函数如下所示:

func handler(w http.ResponseWriter, r *http.Request) {
    data, _ := ioutil.ReadFile("web/index.html")
    w.Write(data)
}

我知道这不是为网站提供服务的最佳方式 提交函数如下所示:

func submit(w http.ResponseWriter, r *http.Request) {
    log.Println("METHOD IS " + r.Method + " AND CONTENT-TYPE IS " + r.Header.Get("Content-Type"))
    r.ParseMultipartForm(32 << 20)
    file, header, err := r.FormFile("uploadFile")
    if err != nil {
        json.NewEncoder(w).Encode(Response{err.Error(), true})
        return
    }
    defer file.Close()

    out, err := os.Create("/tmp/file_" + time.Now().String() + ".png")
    if err != nil {
        json.NewEncoder(w).Encode(Response{err.Error(), true})
        return
    }
    defer out.Close()

    _, err = io.Copy(out, file)
    if err != nil {
        json.NewEncoder(w).Encode(Response{err.Error(), true})
        return
    }

    json.NewEncoder(w).Encode(Response{"File '" + header.Filename + "' submited successfully", false})
}

问题是执行提交函数时,r.MethodGETr.Header.Get("Content-Type") 是一个空字符串,然后它会继续,直到第一个 if where r.FormFile 返回以下错误: 请求内容类型不是 multipart/form-data 我不明白为什么 r.Method 总是 GET 而没有 Content-Type。我尝试以多种不同的方式来处理 index.html,但 r.Method 始终为 GET 且 Content-Type 为空。这是 index.html 中上传文件的函数:

function upload() {
    var formData = new FormData();
    formData.append('uploadFile', document.querySelector('#file-input').files[0]);
    fetch('/submit', {
        method: 'post',
        headers: {
          "Content-Type": "multipart/form-data"
        },
        body: formData
    }).then(function json(response) {
        return response.json()
    }).then(function(data) {
        window.console.log('Request succeeded with JSON response', data);
    }).catch(function(error) {
        window.console.log('Request failed', error);
    });
}

这是 HTML:

<input id="file-input" type="file" name="uploadFile" />

请注意,标签不在标签内,我认为这可能是问题所在,所以我将函数和 HTML 都更改为如下内容:

function upload() {
    fetch('/submit', {
        method: 'post',
        headers: {
          "Content-Type": "multipart/form-data"
        },
        body: new FormData(document.querySelector('#form')
    }).then(function json(response) {
        return response.json()
    }).then(function(data) {
        window.console.log('Request succeeded with JSON response', data);
    }).catch(function(error) {
        window.console.log('Request failed', error);
    });
}

<form id="form" method="post" enctype="multipart/form-data" action="/submit"><input id="file-input" type="file" name="uploadFile" /></form>

但这也不起作用。我在 Google 上搜索了如何使用 fetch() 以及如何接收来自 go 的文件上传,我发现它们与我的非常相似,我不知道我做错了什么。

更新: 使用 curl -v -F 'uploadFile=@\"C:/Users/raul-/Desktop/test.png\"' http://localhost:8080/submit 我得到以下输出:

* Trying ::1...
* Connected to localhost (::1) port 8080 (#0)
> POST /submit HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.45.0
> Accept: */*
> Content-Length: 522
> Expect: 100-continue
> Content-Type: multipart/form-data; boundary=---------------------------a17d4e54fcec53f8
>
< HTTP/1.1 301 Moved Permanently
< Location: /submit/
< Date: Wed, 18 Nov 2015 14:48:38 GMT
< Content-Length: 0
< Content-Type: text/plain; charset=utf-8
* HTTP error before end of send, stop sending
<
* Closing connection 0

我运行 go run main.go 的控制台在使用 curl 时不输出任何内容。

最佳答案

我设法解决了我的问题,所以放在这里以防其他人需要它。并感谢@JiangYD 提供使用 curl 测试服务器的提示。

长话短说

  • 我写了 http.HandleFunc("/submit/", submit) 但我正在向 /submit 发出 POST 请求(注意缺少的斜杠)<< This由于重定向很重要
  • 不要自己指定 Content-Type,浏览器会为您指定

长答案

我按照@JiangYD 所说的做了,并使用curl 来测试服务器,我用响应更新了我的答案。我发现奇怪的是有一个 301 重定向,因为我没有把它放在那里,我决定使用以下 curl 命令

curl -v -F 'uploadFile=@\"C:/Users/raul-/Desktop/test.png\"' -L http://localhost:8080/submit

(注意 -L)这种方式 curl 跟随重定向,尽管它再次失败,因为在重定向时,curl 从 POST 切换到 GET 但通过该响应我发现对 /submit 被重定向到 /submit/ 我记得我在 main 函数中是这样写的。

修复它仍然失败后,响应是 http: no such file 并且通过查看 net/http 代码我发现这意味着该字段没有'不存在,所以我对获得的所有字段名称进行了快速测试:

for k, _ := range r.MultipartForm.File {
    log.Println(k)
}

我将 'uploadFile 作为字段名称,我删除了 curl 命令中的单引号,现在它完美地上传了文件

但这并没有就此结束,我现在知道服务器工作正常,因为我可以使用 curl 上传文件,但是当我尝试通过托管网页上传文件时出现错误: Content-Type 中没有多部分边界参数

所以我发现我应该在标题中包含边界,我将 fetch 更改为如下内容:

fetch('/submit', {
    method: 'post',
    headers: {
        "Content-Type": "multipart/form-data; boundary=------------------------" + boundary
    }, body: formData})

我这样计算边界:

var boundary = Math.random().toString().substr(2);

但我仍然得到一个错误:multipart: NextPart: EOF 那么如何计算边界呢?我阅读了规范 https://html.spec.whatwg.org/multipage/forms.html#multipart/form-data-encoding-algorithm并发现边界是通过对文件进行编码的算法计算的,在我的例子中是 FormData,FormData API 没有公开获取该边界的方法,但我发现浏览器添加了 Content-Type 和 multipart/form-data 和边界,如果你没有指定它,所以我从 fetch 调用中删除了 headers 对象,现在它终于可以工作了!

关于javascript - 使用 go 处理文件上传,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33771167/

相关文章:

javascript - 如何向图像的某些部分添加多个链接

html - 在 lion 中禁用 webview 的橡皮筋滚动

javascript - 为什么要求总是返回带有新内部引用的新对象

javascript - 根据cookie值打开iframe

javascript - React 组件不刷新或重新渲染

css - 在页脚中将我的图标 float 到右侧

jquery - 如何将自定义 CSS Modal 转换为 Bootstrap Modal?

http持久连接和ssl session

http - 如何通过http协议(protocol)使用ffmpeg进行流式传输

http - HAProxy 将 http 重定向到 https (ssl)