php extjs 文件上传,ExtJs使用ajax跨域上传文件

搜索热词

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

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

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

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');

}

});测试运行正常,服务器也可以正确获取文件信息,问题得到解决。

总结

如果觉得编程之家网站内容还不错,欢迎将编程之家网站推荐给程序员好友。

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值