在实际工作中产生了一个需求。我们负责A系统,B系统为旧系统有相应的权限控制用session来判断登录。
用户会在一系列的人工操作后得到一个html页面的数据,要求我们把这个页面的内容记录下来并提交到A系统中。
由于人工操作和session权限控制的问题。我首先放弃了服务器端的httprequest,webservice之类的方法。
由于A系统为一个BS架构的系统,所以不到万不得已不考虑,winform程序的解决方案。
由此,我打算用ajax、JS来解决问题。
首先是获取异主域数据。不论用什么方法,这个都有一个跨域访问的问题。使用jsonp就只支持GET也没有办法,访问二进制数据。
所以用同域代理页是不可避免的。所幸我们可以有B系统的管理权可以在上面放html网页。
所以我们在上面放了一个网页src_prosy.html。
其主要页面元素有
<input type="button" value="得到正文" οnclick="javascript:getHtml();" />
<input type="button" value="得到图片" οnclick="javascript:getImg1();" />
<input type="button" value="得到图片2" οnclick="javascript:getImg2();" />
<form id="form1" method="post" action="self_get_html_and_img.ashx">
<textarea id="textarea_htmltext" cols="20" rows="20" ></textarea>
<textarea id="textarea_image1" cols="20" rows="20" ></textarea>
</form>
<iframe id="iframe1" src="src_login.aspx" width="300px" height="300px"></iframe>
我们首先访问这一页,然后在iframe中完成B系统的登录,人工操作等行为,最终来到了目的页,比如 getTable.asp?id=1233155436236876
下面开始进行数据获取的测试。
首先使用document.documentElement.outerHTML;
来获取页面的html正文文本,没有问题。
function GetIframeInnerHtml(IFrameID)
{
return document.getElementById(IFrameID).contentWindow.document.documentElement.outerHTML;
}
function getHtml()
{
$("#textarea_htmltext").val(GetIframeInnerHtml("iframe1"));
}
问题来了,在这个正文中有两个图片。我需要得到这两个图片的内容。
jquery是肯定不行了。没办法,直接使用XMLHttpRequest
function getImg1()
{
$("#iframe1").contents().find("IMG").each(function(){
theimageurl = $(this).attr("src");
var myDate = new Date();
var url = theimageurl + "?time=" + myDate.toLocaleString();
var xmlhttp;
if (window.XMLHttpRequest)
{// code for IE7+, Firefox, Chrome, Opera, Safari
xmlhttp=new XMLHttpRequest();
}
else
{// code for IE6, IE5
xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
}
xmlhttp.open("GET",url,true);
xmlhttp.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
xmlhttp.onreadystatechange = function() {
if (xmlhttp.readyState == 4 && xmlhttp.status==404)
{
alert('图片数据加载失败:<br />错误信息,访问数据不存在!<br />' + url);
return;
}
if (xmlhttp.readyState == 4 && xmlhttp.status == 200)
{
//在这里的数据要处理
}
}
xmlhttp.send();
});
}
又出现了问题,首先
var theValue = xmlhttp.responseBody IE 会报错
var theValue = xmlhttp.responseStream 没报错,但在后面发送时,表示对象为空或不是对象。
var theValue = xmlhttp.responseText 报c00ce514错误。
这儿产生的错误和我试图进行的解决方案已经记不太清楚了,我好象记得xmlhttp.responseStream可以得到数据,但转Send的时候数据被截断了,好象是碰到0/这种字符串结束符,数据就断开了,自己写一个函数来完成byte[] to Base64String 实在是最后的选择。
所以,如何获取二进制数据并且进行编码就成为了一个大问题。
所以我在csdn上发贴问了下,非常自然的,没有任何有效回答。话说我已经连续三个问题在csdn无解了。.net大版已经快成纯静水工厂了。
以上的牢骚话。下面继续。
有网友介绍使用一套脚本。
/**
* 将符合字节流的string转化成Blob对象
*
* @param {String} data
* @return {Blob}
* @api public
*/
function binaryToBlob(data) {
var bb = new BlobBuilder();
var arr = new Uint8Array(data.length);
for(var i = 0, l = data.length; i < l; i++) {
arr[i] = data.charCodeAt(i);
}
bb.append(arr.buffer);
return bb.getBlob();
};
/**
* 根据URL获取图片的Blob对象
*
* @param {String} url
* @return {Blob}
* @api public
*/
function getImageBlob(url) {
var r = new XMLHttpRequest();
r.open("GET", url, false);
// 详细请查看: https://developer.mozilla.org/En/XMLHttpRequest/Using_XMLHttpRequest#Receiving_binary_data
// XHR binary charset opt by Marcus Granado 2006 [http://mgran.blogspot.com]
r.overrideMimeType('text/plain; charset=x-user-defined');
r.send(null);
var blob = binaryToBlob(r.responseText);
blob.name = blob.fileName = url.substring(url.lastIndexOf('/') + 1);
blob.fileType = "image/jpeg"; //"image/octet-stream";
return blob;
};
/**
* 将dataUrl转化成Blob对象
*
* @param {String} dataurl
* @return {Blob}
* @api public
*/
function dataUrlToBlob(dataurl) {
// data:image/jpeg;base64,xxxxxx
var datas = dataurl.split(',', 2);
var blob = binaryToBlob(atob(datas[1]));
blob.fileType = datas[0].split(';')[0].split(':')[1];
blob.name = blob.fileName = 'pic.' + blob.fileType.split('/')[1];
return blob;
};
好心人提供的脚本,又一次印证了IE的不靠谱,以上是FF的。IE中是不可用的,主要有以下几个问题。
首先
r.overrideMimeType('text/plain; charset=x-user-defined');
这个overrideMimeType在IE7中是不支持的高版本的我没试。
其次
其中最为关键的 BlobBuilder 对象,对于IE来说仅在IE10的Beta2中才支持。魂淡啊,我才用IE7!
好了,自己解决吧。除了盗版和八卦以外,度娘是不感兴趣的,google到了。
var xmldom = new ActiveXObject('MSXML2.DOMDOCUMENT');
xmldom.async = false;
var base64 = xmldom.createElement('base64');
base64.dataType = 'bin.base64';
base64.nodeTypedValue = xmlhttp.responseBody;
$("#textarea_image1").val(base64.text);
这个xmldom没有问题可以成功得到并使用base64编码来编码数据。
把这个得到base64编码字符串,放到一个textarea中提交,self_get_html_and_img.ashx
string Base64Str = context.Request["text1"].ToString();
MemoryStream ms = new MemoryStream(Convert.FromBase64String(Base64Str));
FileStream fs = new FileStream(AppDomain.CurrentDomain.BaseDirectory + "/a.jpg", FileMode.Create, FileAccess.ReadWrite);
ms.WriteTo(fs);
fs.Close();
没问题测试成功。
后继要解决的是,文本长度超出64k限定IE的input控件不支持的问题,最终是把文本截断,生成几个hidden来存放,再提交,这才最终的解决了问题。