JQuery表单插件文件上传使用POST重定向到POST响应

5

请帮忙,这是一个严重的阻碍问题!

我正在开发一个使用NodeJS + jQuery表单插件 + Typescript的项目,尝试进行文件上传。上传后,服务器会发送POST消息的响应,并在屏幕上呈现。在POST响应在屏幕上呈现之前,文件确实已经成功上传完毕。我期望POST响应会调用“success”函数,而不是重定向页面以显示JSON响应。

以下是代码:

$(new ID().setAsRoot().selectId()).append(
    "<form id=\"fileUploadForm\" accept-charset=\"utf-8\" method=\"post\" action=\"/upload\" enctype=\"multipart/form-data\">" +
        "<input id = \"filename\" type=\"file\" name=\"userfile\" multiple=\"multiple\" />" +
        "<button type=\"submit\" id = \"submitButton\"> Upload </button></form>");

var form = $("#fileUploadForm");
form.submit(function (e) {
    //e.preventDefault();
    this.ajaxSubmit({
        type: form.attr('method'),
        url: form.attr('action'),
        data: form.serialize(),
        success: function (data) {
            var x = JSON.parse(data);
            alert("Success : " + x);
        },
        error: function (data) {
            var x = JSON.parse(data);
            alert("Error : " + x);
        }
    });
});

成功函数没有被调用(这意味着警报消息不会显示)。JSON数据只是以这种方式呈现在屏幕上:
{
  "path": "data\\cb3409f1cc234ec3f64b60d80be13a3e.html",
  "name": "feed.html"
}

控制台上显示了一个错误,内容如下:

Uncaught SyntaxError: Unexpected token : shortcut_manager.js:123
(anonymous function) shortcut_manager.js:123
(anonymous function) extensions::messaging:327
Event.dispatchToListener extensions::event_bindings:386
Event.dispatch_ extensions::event_bindings:371
Event.dispatch extensions::event_bindings:392
dispatchOnMessage

这是处理它的服务器端代码。服务器使用NodeJS formidable模块。
public upload(req:express.Request, res) {

        var form = new formidable.IncomingForm();
        var originalFileName:String;
        var filePath:String;
        form.uploadDir = this.directory;
        form.keepExtensions = true;
        form.type = 'multipart';
        var fields = [];
        form
            .on("error", function (err) {
            })
            .on("field", function (field, value) {
            })
            .on("end", function () {
                res.send({
                    path: filePath,
                    name: originalFileName
                });
            })
            .on("file", function (name, file:formidable.File) {
                originalFileName = file.name;
                filePath = file.path;
            });
        form.parse(req);
        return;
    }

--更新--

如果我使用$.ajax而不是this.ajax。文件将不会上传。浏览器控制台显示错误:

XMLHttpRequest cannot load http://localhost/. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:8080' is therefore not allowed access. 

你不需要在响应中添加状态码吗? - Paul D.
在POST响应中添加了状态码 - 仍然无法工作 - 请参见上面更新的代码。谢谢。 - EternallyCurious
你是指来自"this.ajaxSubmit()"的"this"吗?那不是一个回调函数。如果我用$(this)替换它,IDE会抛出错误:"参数类型与参数不匹配"。谢谢。 - EternallyCurious
我在浏览器控制台上遇到了一个错误 - 如上所示。在开发控制台上,我得到了“Error: Can't set headers after they are sent.”的错误,正如我在问题中提到的那样。谢谢。 - EternallyCurious
1
听起来表单正常提交了。你在控制台中有收到任何错误吗?此外,我认为应该是$.ajax而不是this.ajax - Tom Pietrosanti
显示剩余5条评论
5个回答

1

根据您的评论:

“谢谢您的建议。$.ajax无法加载文件。它会显示一个错误,您可能已经知道了。尽管您的怀疑是正确的,因为当我使用“this.ajax”时,文件确实被上传,但控制台会显示一个错误“Type has no method ajax”。

  • 您不能像通常上传表单数据一样使用$.ajax()上传文件。
  • 由于使用this.ajax时出现错误,所以表单会被提交。这就是为什么您在浏览器中看到JSON响应的原因。

问题是this.ajax从哪里来?您是否从使用您未包含的库的示例代码中复制了代码?

为了通过AJAX上传文件,您需要某种插件(自己做需要一些努力,特别是如果您需要支持旧版浏览器)。

更新

提交处理程序应如下:

form.submit(function (e) {
  e.preventDefault();
  $(this).ajaxSubmit().done(function (data) {
    var x = JSON.parse(data);
    alert("Success : " + x);
  }).fail(function (data) {
    var x = JSON.parse(data);
    alert("Error : " + x);
  });
});

解释:由于您想调用jQuery插件,所以需要一个jQuery对象,因此使用$(this)ajaxSubmit()不需要任何参数(如果您想要,可以传递选项)。因为我没有传递参数,所以回调函数被附加在此处,包括donefail(而不是successerror)。


谢谢您的评论。我修改了这个问题并添加了更多细节。我正在使用 JQuery 表单插件进行上传。请再次查看问题。非常感谢您的帮助。 - EternallyCurious
@EternallyCurious 有数百个表单插件可供选择,你用哪一个?顺便说一下,大多数插件都无法处理文件上传。 - a better oliver
@zeroflagL,我按照你的建议修改了代码。虽然"$(this)"会抛出错误,所以我只使用"this"。回调函数done和fail没有被调用。除此之外,页面不再重定向到JSON响应,这也是我发布这个问题的核心问题。所以赏金归你所有。非常感谢你的努力。 - EternallyCurious
@zeroflagL 实际上我正在使用 "form.ajaxSubmit" 而不是 "this.ajaxSubmit" 或 "$(this).ajaxSubmit"。使用 "this" 根本不会上传文件。 - EternallyCurious

0

将 this.ajax("http://..." 替换为 url:form.attr('method')

  var form = $("#fileUploadForm");     
  $.ajax({
      type:form.attr('method'),
       url:form.attr('action'),
      data:form.serialize(),
             success: function(data){
      }});

谢谢,这是一个非常有用的方法,我已经相应地更改了我的代码 - 但结果并没有改变。仍然发生完全相同的事情。也许你需要从服务器端了解一些信息。我也发布了一些服务器端代码。 - EternallyCurious

0
你是在问为什么我会收到这个错误吗:
XMLHttpRequest cannot load localhost/. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'localhost:8080' is therefore not allowed access.
如果是的话,那么你需要在本地主机(假设也是 Node)的响应中设置一个头来允许来自 localhost:8080 的源访问此资源,像这样:
res.setHeader('Access-Control-Allow-Origin', 'http://localhost:8080');

或许你实际上是在尝试从同一台服务器请求,但忘记了 8080 是你的测试服务器:如果是这样,在你的测试中应该将 URL 列为

$.ajax("http://localhost:8080", { //....

或者重复当前源:

$.ajax( window.location.origin,{ // ....

如果您无法在响应中设置Origin策略,那么避免这种情况的最后一种选择是使用jsonp服务,该服务不太严格但也不太功能齐全,但这对于您的任务来说并不合适,因此我不会深入介绍。

无论如何,一旦您通过了Origin策略,就会遇到@zeroflagL提到的其他障碍。 这里有一种方法可能适合您:如何处理跨域iframe文件上传json响应?


感谢您的评论。我已经修改了这个问题。我正在使用JQuery表单插件进行上传。请再看一下。非常感谢您的帮助。 - EternallyCurious
@EternallyCurious,如果你仍然遇到访问控制错误,那么无论你计划如何上传文件,你仍需要查看我上面的答案。 - gillyspy

0

我认为你的客户端代码存在几个不同的问题。

我看到你定义了$form,但我从未看到你定义过form。然后,你调用了form.attr(...)。除非你在其他地方定义了form,否则这将立即引发错误。

更重要的是,标准的$.ajax()无法在旧版本的浏览器上上传文件(即使是最近的IE 9!)。XHR文件上传的功能是在XHR2中添加的(请参见CanIUse)。如果你希望你的Web应用程序与任何下级浏览器兼容,你需要以某种方式解决这个限制。

好消息是,有各种插件可以为您完成大部分工作。如果您使用的是大于1.6版本的jQuery,我建议您查看jQuery文件上传插件。它在可用时使用新的XHR文件上传功能,并在不支持新功能的浏览器中回退到iFrame传输。虽然iFrame传输很简单聪明,但您实际上不需要了解任何关于它的知识,因为插件将其抽象化了。该插件完全免费。

基本插件将为您完成繁琐的工作,而完整版甚至还具有内置UI。还有很多关于如何实现服务器端方法的详细信息。基本语法非常简单:

$("#fileUploadForm").fileupload()

如果您正在寻找更轻量级的东西,iFrame Transport Plugin 可能就是您所需要的。(它也是免费的。)我今天刚在一个项目中实现了它。它是一个非常好文档化的 JS 文件。它增强了 $.ajax() 函数的标准功能。关键是在 AJAX 选项列表中指定 iframe: true。您的 ajax 调用可能看起来像这样:
$.ajax("http://localhost", {
    type: $form.attr('method'),
    url: $form.attr('action'),
    data: $form.serialize(),
    dataType: "JSON",
    iframe: true,
    files: $("#MyFileInputId"),
    success: function (data) {
        alert("Success : " + data);
    },
    error: function (data) {
        alert("Error : " + data);
    }
});

顺便说一下,如果你想要确保非 AJAX POST 不会发生,你可以在代码中再加上一行:
$form.submit(function (e) {
    e.preventDefault();
    // do stuff
    return false;
}

preventDefault()会取消POST请求,即使在随后的代码中发生错误。


感谢您的评论。我修改了这个问题,因为问题的一小部分已经解决了,并添加了更多细节。我正在使用JQuery表单插件进行上传。请再次查看问题。感谢您的帮助。PS. preventDefault()会完全阻止上传。 - EternallyCurious
这就是 preventDefault() 的预期结果。如果没有这行代码,因为 AJAX 发送请求时出错并且没有到达 return false 语句,浏览器仍会上传文件。这将允许浏览器执行默认操作(标准的非 AJAX HTML 表单提交)。 - Michael Richardson

0
var form = $("#fileUploadForm");     
 $.ajax({
      type:form.attr('method'),
      url:form.attr('action'),
      dataType: 'json', //  Expect a return value of json
      data:form.serialize(),
             success: function(data){
      }});

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接