Ext.define('test.view.uploadtest.main',{
    extend:'Ext.panel.Panel',xtype:'uploadtest',items: [{
        xtype:'form',title:'upload test',width:360,layout:'vBox',items: [{
            xtype:'filefield',width:'100%',name:'logfile',id:'logfile',},{
            xtype:'button',width:45,text:'上传文件',listeners: {
                click:function (btn,e,opts) {
                    fileEl = Ext.getCmp('logfile').fileInputEl.dom;
                    var fd =new FormData();
                    fd.append('logfile',fileEl.files[0]);
                    Ext.Ajax.request({
                        url:'http://192.168.21.101/u.PHP',cors:true,useDefaultXhrHeader:false,method:'post',rawData:fd,headers: {
                            "Content-Type":null   /* 最重要的部分,将Content-Type设置成null,ExtJs则会由内部的XMLHTTPRequest自动生成包含boundary的Content-Type,否则在使用rawData的情况下,ExtJs会将Content-Type设置成text/plain */
                        },success:function (res,opts) {
                            alert('success');
                        },failure:function (res,opts) {
                            alert('failure');
                        }
                    });
                }
            }
        }]
    }]
});

以上是可以跨域上传文件的代码,关键地方做了标注。
以下是太长不看版:
刚开始的时候,我使用form的submit来提交文件:

btn.up('form').getForm().submit({
    url:'http://192.168.21.101/u.PHP',cors: true,success:function(form,action){
        alert('success');
    },failure:function(form,action){
        alert('failure');
    }
});
发现每次执行都是failure,再查看failure事件中的action.response可以看到:
responseText:"{success:false,message:"Blocked a frame with origin "http://localhost:1841" from accessing a cross-origin frame."}"
responseXML:null
status:400
statusText:"Blocked a frame with origin "http://localhost:1841" from accessing a cross-origin frame."
由于跨域,即使上传文件的请求成功,也会由于浏览器的拦截导致失败。既然form的submit方法不支持跨域,那就改用ajax+formdata来请求,在ExtJs中formdata需要使用到rawData属性来发送:
fileEl = Ext.getCmp('logfile').fileInputEl.dom;
var fd =newFormData();
fd.append('logfile',fileEl.files[0]);
Ext.Ajax.request({
    url: 'http://192.168.21.101/u.PHP',useDefaultXhrHeader: false,method: 'post',rawData: fd,success: function (res,opts) {
        alert('success');
    },failure: function (res,opts) {
        alert('failure');
    }
});
运行时,所有的请求都从success的事件中返回了,然而服务器并没有正确接收到文件,查看网络抓包发现了问题的所在,这个是请求的HTTP头部:

Accept:*/*
Accept-Encoding:gzip,deflate
Accept-Language:en-US,en;q=0.8,zh-CN;q=0.6,zh;q=0.4
Connection:keep-alive
Content-Length:621
Content-Type:text/plain <-------------------错误的Content-Type将导致服务器无法正确解析参数
Host:192.168.21.101
Origin:http://localhost:1841
Referer:http://localhost:1841/
User-Agent:Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML,like Gecko) Chrome/59.0.3071.115 Safari/537.36
注意看Content-Type的地方,这个请求的Content-Type是text/plain,而传输文件的时候,数据是通过multipart/form-data的格式来传输
------WebKitFormBoundaryBKkxWkenCjSNoZr0
Content-disposition: form-data; name="logfile"; filename="b.txt"
Content-Type: text/plain

------WebKitFormBoundaryBKkxWkenCjSNoZr0--
这就解释了为什么服务器无法正确处理数据,根据rfc1867协议中的规定,当使用表单上传文件的时候,需要使用multipart/form-data的格式进行编码,并且在http的头部中,设置Content-Type为multipart/form-data,并且使用负载数据中不会出现的字符串,作为boundary用于字段之间进行分割。正确的Content-Type应该是:
Content-Type:multipart/form-data; boundary=AaB03x
然而当我使用headers属性手动设置Content-Type的时候出现了一个无奈的问题:boundary不能手动设置。
我尝试了两种方法,
1.不手动加boundary
Ext.Ajax.request({
    url: 'http://192.168.21.101/u.PHP',headers:{
        'Content-Type':'multipart/form-data'
    },opts) {
        alert('failure');
    }
});
那最后执行请求时,http头部的Content-Type将不包含boundary信息,服务器仍然无法正确解析数据:
Accept:*/*
Accept-Encoding:gzip,zh;q=0.4
Connection:keep-alive
Content-Length:621
Content-Type:multipart/form-data   <------------------注意这行,不包含boundary信息
Host:192.168.21.101
Origin:http://localhost:1841
Referer:http://localhost:1841/
User-Agent:Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML,like Gecko) Chrome/59.0.3071.115 Safari/537.36

2.手动加上boundary:

Ext.Ajax.request({
    url: 'http://192.168.21.101/u.PHP',headers:{
        'Content-Type':'multipart/form-data; boundary=----fortestboundaryAaSsCdes'
    },opts) {
        alert('failure');
    }
});

发现请求的Http头部中的boundary和内容中的boundary不一致:

Accept:*/*
Accept-Encoding:gzip,zh;q=0.4
Connection:keep-alive
Content-Length:621
Content-Type:multipart/form-data; boundary=----fortestboundaryAaSsCdes   <------Content-Type中已经有了我们手动设置的boundary
Host:192.168.21.101
Origin:http://localhost:1841
Referer:http://localhost:1841/
User-Agent:Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML,like Gecko) Chrome/59.0.3071.115 Safari/537.36
然而内容中的boundary仍然是浏览器自己生成的:
------WebKitFormBoundarycWsyuhDfyRmYp8dZ
Content-disposition: form-data; name="logfile"; filename="b.txt"
Content-Type: text/plain


------WebKitFormBoundarycWsyuhDfyRmYp8dZ--
这个意味着boundary必须由浏览器生成,那么我们就不能手动设置Content-Type,但是不设置Content-Type,ExtJs会自己设置Content-Type为text/plain,问题似乎陷入了死胡同。
我相信问题必定有解决的方法,最糟糕的情况不过于需要修改ExtJs的源码,然而当我仔细阅读这部分的源码的时候,发现ExtJs还是留了一道后门给我们,解决这个问题的关键就在Ext.data.request.Ajax这个类的setupHeaders中:
setupHeaders:function(xhr,options,data,params) {
    var me = this,// 当Ajax.request中的参数对象有headers这个参数的时候,headers使用传入的参数
    // 注意type在这个地方是'Content-Type' 
    headers = Ext.apply({},options.headers || {},me.defaultHeaders),type = 'Content-Type',... 

    // 接下来ExtJs检查了headers中间是否设置了'Content-Type'这个属性,如果没有设置 
    // 并且使用了rawData、xmlData和jsonData中的一个,则将设置默认的Content-Type 
    if (!headers.hasOwnProperty(type) && (data ||params)) { 
        if (data) { 
            if (options.rawData) { 
                // 这个地方可以看到,当使用rawData时,Content-Type将被默认设置成'text/plain' 
                // 这个和我们之前测试代码的运行结果是一致的 
                contentType ='text/plain'; 
            } 
            else { 
                if (xmlData &&Ext.isDefined(xmlData)) { 
                    contentType ='text/xml'; 
                } 
                else if (jsonData && Ext.isDefined(jsonData)) { 
                    contentType ='application/json'; 
                } 
            } 
        } 
        headers[type] =contentType; 
    }

... 

    // 最关键的代码出现在这个地方,如果headers['Content-Type']没有定义,或者设置为null 
    // 那么这个属性将会从headers里删去 
    if (headers[type] ===undefined ||headers[type] ===null) { 
        delete headers[type]; 
    } 

    try { 
        // 删去之后,ExtJs将不会再设置Content-Type,而是让XMLHTTPRequest自己决定Content-Type的内容 
        // 这个正是我们所需要的 
        for (key in headers) { 
            if (headers.hasOwnProperty(key)) { 
                header = headers[key]; xhr.setRequestHeader(key,header); 
            } 
        } 
    }

...

    return headers;
},
这份代码中我隐藏了部分不太相关的代码,分析了相关代码,我们发现,只需要在headers属性中设置'Content-Type'为null,那么ExtJs在发送rawData时,也不会自己去设置Content-Type为'text/plain'。
既然这样,我把上面通过ExtJs的ajax发送数据的代码稍作修改:
Ext.Ajax.request({
    url: 'http://192.168.21.101/u.PHP',headers:{
        'Content-Type':null
    },opts) {
        alert('failure');
    }
});
测试运行正常,服务器也可以正确获取文件信息,问题得到解决。

ExtJs使用ajax跨域上传文件的更多相关文章

  1. ios – OpenCV构建问题,找不到ext/atomicity.h

    我得到编译器错误抱怨在构建包含OpenCV的项目时.环境是针对iOS的Xcode4.5.它为模拟器编译良好,但在为设备构建时失败.这是错误文本:我正在使用opencv2.framework,使用指令here构建cmake.解决方法默认情况下,XCode4.5使用libc(支持C11的LLVMC标准库)生成要构建的新项目.但OpenCV期望针对GNUlibstd

  2. 应用程序关闭时的iOS任务

    我正在构建一个应用程序,通过ajax将文件上传到服务器.问题是用户很可能有时不会有互联网连接,并且客户希望在用户重新连接时安排ajax调用.这可能是用户在离线时安排文件上传并关闭应用程序.应用程序关闭时可以进行ajax调用吗?

  3. Android Studio 2.3更新:警告:使用不兼容的插件进行注释处理:android-apt.这可能会导致意外行为

    所以,我已经阅读了本网站上有关此问题的所有问题.我还与其中一位有类似问题的开发人员聊天,他能够解决这个问题.我没有在我的gradle脚本中编写apt或annotationProcessor.我的代码中没有任何地方写过android-apt这个词.我甚至继续检查了所有的库.这包含在我的项目中.这是一个非常大的问题,需要解决.我在下面附加修改后的build.gradle,请建议:解决方法较旧版本的Re

  4. 如何在Picasso库中实现Pinch Zoom?

    我在我目前的项目http://square.github.io/picasso/中使用Picasso库.一切都运行正常,但我无法弄清楚如何为从URL加载的所有图像实现PinchZoom.说实话,我甚至不知道在哪里放置onClickListener.我的应用程序有几个片段,每个片段都有2个选项卡,第一个选项卡有一个ListView,第二个选项卡有一些显示在GridView中的图片:Bmw.javaS

  5. android – Phonegap本地构建 – jquery ajax错误:readystate 0 responsetext status 0 statustext error

    解决方法您是否在索引文件中包含了内容安全元标记?

  6. Ajax简单的异步交互及Ajax原生编写

    一提到异步交互大家就会说ajax,仿佛ajax这个技术已经成为了异步交互的代名词.那下面将研究ajax的核心对象

  7. Ajax跨域问题的解决办法汇总(推荐)

    本文给大家分享多种方法解决Ajax跨域问题,非常不错具有参考借鉴价值,感兴趣的朋友一起学习吧

  8. ajax编写简单的登录页面

    这篇文章主要为大家详细介绍了ajax编写简单登录页面的具体代码,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  9. ajax从JSP传递对象数组到后台的方法

    今天小编就为大家分享一篇ajax从JSP传递对象数组到后台的方法,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧

  10. 解决ajax返回验证的时候总是弹出error错误的方法

    这篇文章主要介绍了解决ajax返回验证的时候总是弹出error错误的方法,感兴趣的小伙伴们可以参考一下

随机推荐

  1. xe-ajax-mock 前端虚拟服务

    最新版本见Github,点击查看历史版本基于XEAjax扩展的Mock虚拟服务插件;对于前后端分离的开发模式,ajax+mock使前端不再依赖后端接口开发效率更高。CDN使用script方式安装,XEAjaxMock会定义为全局变量生产环境请使用xe-ajax-mock.min.js,更小的压缩版本,可以带来更快的速度体验。

  2. vue 使用 xe-ajax

    安装完成后自动挂载在vue实例this.$ajaxCDN安装使用script方式安装,VXEAjax会定义为全局变量生产环境请使用vxe-ajax.min.js,更小的压缩版本,可以带来更快的速度体验。cdnjs获取最新版本点击浏览已发布的所有npm包源码unpkg获取最新版本点击浏览已发布的所有npm包源码AMD安装require.js安装示例ES6Module安装通过Vue.use()来全局安装示例./Home.vue

  3. AJAX POST数据中文乱码解决

    前端使用encodeURI进行编码后台java.net.URLDecoder进行解码编解码工具

  4. Koa2框架利用CORS完成跨域ajax请求

    实现跨域ajax请求的方式有很多,其中一个是利用CORS,而这个方法关键是在服务器端进行配置。本文仅对能够完成正常跨域ajax响应的,最基本的配置进行说明。这样OPTIONS请求就能够通过了。至此为止,相当于仅仅完成了预检,还没发送真正的请求呢。

  5. form提交时,ajax上传文件并更新到&lt;input&gt;中的value字段

  6. ajax的cache作用

    filePath="+escape;},error:{alert;}});解决方案:1.加cache:false2.url加随机数正常代码:网上高人解读:cache的作用就是第一次请求完毕之后,如果再次去请求,可以直接从缓存里面读取而不是再到服务器端读取。

  7. 浅谈ajax上传文件属性contentType = false

    默认值为contentType="application/x-www-form-urlencoded".在默认情况下,内容编码类型满足大多数情况。在这里,我们主要谈谈contentType=false.在使用ajax上传文件时:在其中先封装了一个formData对象,然后使用post方法将文件传给服务器。说到这,我们发现在JQueryajax()方法中我们使contentType=false,这不是冲突了吗?这就是因为当我们在form标签中设置了enctype=“multipart/form-data”,

  8. 909422229_ajaxFileUpload上传文件

    ajaxFileUpload.js很多同名的,因为做出来一个很容易。我上github搜AjaxFileUpload出来很多类似js。ajaxFileUpload是一个异步上传文件的jQuery插件传一个不知道什么版本的上来,以后不用到处找了。语法:$.ajaxFileUploadoptions参数说明:1、url上传处理程序地址。2,fileElementId需要上传的文件域的ID,即的ID。3,secureuri是否启用安全提交,默认为false。4,dataType服务器返回的数据类型。6,error

  9. AJAX-Cache:一款好用的Ajax缓存插件

    原文链接AJAX-Cache是什么Ajax是前端开发必不可少的数据获取手段,在频繁的异步请求业务中,我们往往需要利用“缓存”提升界面响应速度,减少网络资源占用。AJAX-Cache是一款jQuery缓存插件,可以为$.ajax()方法扩展缓存功能。

  10. jsf – Ajax update/render在已渲染属性的组件上不起作用

    我试图ajax更新一个有条件渲染的组件。我可以确保#{user}实际上是可用的。这是怎么引起的,我该如何解决呢?必须始终在ajax可以重新呈现之前呈现组件。Ajax正在使用JavaScriptdocument.getElementById()来查找需要更新的组件。但是如果JSF没有将组件放在第一位,那么JavaScript找不到要更新的内容。解决方案是简单地引用总是渲染的父组件。

返回
顶部