跨域下载图片走的各种弯路和遇到的种种困难

首先声明 ,我是编程菜鸟,刚做编程不久。欢迎拍砖

要实现的功能

我们一旅游网站(南北游),可以发表游记。富文本,写攻略,上传图片(我们会对图片进行剪切分成大中小等各种图片,方便不同地方调用)。

但有一种情况是用户会把自己发表在其他网站的文章复制粘贴到我们网站,结果是我们只存下了图片的链接地址,而没有实际保存图片。在其他地方用到该图片的时候就会出现图片大小不合适或者显示挂图的现象。

解决的办法就是根据用户粘贴的地址,将图片下载到我们的服务器上。然后将文本框中其他网站地址换成自己网站的地址,保存。要实现这个功能可算是费了一番周章。

第一次尝试的办法:

       在保存之前 使用jquery对富文本框中的图片进行遍历,发现不是我们网站的图片就将其地址发到后台处理,异步提交使用$.post。

大概的一个代码

$("#kecontent>img").each({
            var src = $(this).attr("src");
            if(src.indexOf("nanbeiyou.com")<=0){
                $.post("http://file.nanbeiyou.com/******&remotePath="+encodeURI(src),
                success:function(){
                  //do some thing
                })  ;
            }

     });

  

  后台根据传过来的地址,模拟浏览器将图片先保存到内容,然后进行切割保存到服务器上。(很快就实现了,发现要下载的图片保存到服务器上,着实高兴了一下,以为快完工了呢)

  改正1

      很快发现问题,程序在www.nanbeiyou.com域下,而下载下来的图片在file.nanbeiyou.com资源域下,$.post不能跨域获得返回来的图片名字。负责人说用iframe跨域获得吧。

我想起jquery 的jsonp方式可以跨域,网上查了查:http://www.cnblogs.com/know/archive/2011/10/09/2204005.html ,然后改用$.ajax jsonp进行异步请求。

调试发现使用jsonp确实可以把图片的名字返回来(加上前边固定的地址就ok了),但是当发了多张图片之后,怎么将图片的名字和它原来的地址对应上,替换原来的其他网站地址呢?于是在发送图片原地址之前,给该图片标签属性加上一个唯一标识数(使用index正合适,不用再使用随机数),然后将图片源地址和index数一起发送到后台,后台处理了图片之后,将图片名称和 这个index再返回来,根据返回的index匹配页面中的img,将其中的地址替换。这想法不错,实现之。

  前台页面代码:

$("#kecon>img").each(function(index){

            var src = $(this).attr("src");
            $(this).attr("random",index);
            if(src.indexOf("nanbeiyou.com")<=0){
                $.ajax({
                  type:"get",
                  async:true,
                  url:"http://file.nanbeiyou.com/*****&remotePath="+encodeURI(src)+"&random="+index,
                  dataType:"jsonp",
                  jsonp: "callbackparam",//传递给请求处理程序或页面的,用以获得jsonp回调函数名的参数名(默认为:callback)
                  jsonpCallback:"success_jsonpCallback",//自定义的jsonp回调函数名称,默认为jQuery自动生成的随机函数名
                  success : function(json){    

                    $("#kecon>img[random=‘"+json[0].random+"‘]").attr("src")=json[0].name;

                 },
                 error:function(){
                   alert(‘fail‘);
                 }

                });
            }
         });

  给<img>加上属性名叫random,后来发现可能这是个jquery保留字,随之将random改成rands。

愚蠢低级的错误总是有:

$("#kecon>img[rands=‘"+json[0].rands+"‘]").attr("src")=json[0].name; 应改成
$("#kecon>img[rands=‘"+json[0].rands+"‘]").attr("src",json[0].name);

后台实现:这是从网上找的,同事给我的,我也不知道出处。

/// <summary>
        /// 根据输入的图片网址,将图片下载到内存中
        /// </summary>
        /// <param name="downAddress"></param>
        /// <returns></returns>
        private static MemoryStream DownLoad(string downAddress)
        {
            MemoryStream ms = new MemoryStream();
            HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(downAddress);
            request.AllowAutoRedirect = false;
            request.Method = "GET";
            request.UserAgent = "Opera/9.25 (Windows NT 6.0; U; en)";

            HttpWebResponse response = (HttpWebResponse)request.GetResponse();
            Stream responseStream = response.GetResponseStream();
            int i;
            byte[] buffer = new byte[2048];
            //delSetProcess pro = new delSetProcess(SetProcess);
            do
            {
                i = responseStream.Read(buffer, 0, 2048);
                ms.Write(buffer, 0, i);
                //this.Invoke(pro);
            }
            while (i > 0);
            response.Close();
            responseStream.Close();
            responseStream.Dispose();
            response = null;
            request = null;
            responseStream = null;
            return ms;
        }

  后台保存图片,调用download。

if (context.Request.Params["pictype"] == "Travel" && context.Request.Params["remotePath"] != null)
                {
                    string oldPath = context.Request.Params["remotePath"];
                    string oldName = oldPath.Substring(oldPath.LastIndexOf(‘/‘)+1);
                    string oldExt = oldPath.Substring(oldPath.LastIndexOf(‘.‘));
                    int index = int.Parse(context.Request.Params["random"]);

                    MemoryStream ms = DownLoad(oldPath);
                   //给保存在本地的图片设定一个随机数名字
                    string newName = PhotoInfo.GeneratID();

                    using (PhotoInfoChain pi = ThumbFactory.getInstancePhotoinfoChain("Travel"))
                    {

                        pi.Stream = ms;
                        //根据名字,将图片切成大中小等图片保存到本地服务器
                        pi.Save(newName);
                        newExt = pi.InfoList[0].ImgFormat;
                        context.Response.ContentType = "text/plain";
                        string callbackFunName = context.Request["callbackparam"];
                        context.Response.Write(callbackFunName + "([{name:\""+newName + newExt+"\",rands:\""+index+"\"}])");

                    };                   

                }

  改正2

使用上边的jquery方法,当往文本框粘贴了两张图片时,打开浏览器调试查看网络信息,是往后台发送了两个jsonp异步请求,每个请求都成功返回了获得了图片新名称和index (0和1),但是$.ajax()中的success方法中得到的json,能得到两次,但是两次的json[0].rands都是1,或者都是0.而不是两个都获得。也不知道是什么原因。哪位高手遇到过,给解释解释。

还有就是使用异步,将多个请求都发出去了,不知道什么时间图片都下载完成,地址替换完成,什么时间将整个游记表单提交保存。所以放弃一个图片地址一个jsonp请求。而是首先对页面文本框进行遍历,将需要替换的所有图片地址放到一个string变量中,用逗号分隔。一次异步请求所有的图片地址发送到后台代码,然后后台代码对发送的来的图片地址string对象解析分隔,对每个地址进行图片下载,然后将图片新名字和图片原地址(跟前台页面中的地址匹配,以便付新值替换)存入json中返回。

改进3

试着以上方法粘贴了10张左右图片,可以成功保存游记,这个过程有点慢,但是能成功,所以在点击保存游记的同时弹出一个小提示“游记正在保存,请稍后。。”原理很简单,有个隐藏div,点击保存之后,该提示div显示出来。

改进4

使用改进3之后,多张图片也保存了,也给用户一个提醒了,原以为这样就ok了。没想到又发现问题了,当粘贴的图片很多时比如50张图片,保存不成功了,一直提示正在保存,也不报错,发出去的jsonp请求没有了回音,没有查看浏览器网络信息,就以为是发送时间之后时间太长,超时了,所以不成功。查查百度、谷歌。$.ajax确实有个timeout参数,更认为是timeout原因。所以给$.ajax设置上timeout参数之后,还是保存失败,也没有任何提醒。 心想保存失败就失败吧,怎么也要给个提示告诉我失败了,别让我等着了,设定的error函数怎么不触发呢?

又是一顿查阅:http://stackoverflow.com/questions/1002367/jquery-ajax-jsonp-ignores-a-timeout-and-doesnt-fire-the-error-event

原来对于jsonp格式的$.ajax请求,对于jquery1.5版本之前,error方法不能触发。我们就用的jquery1.4,它怎么就这么寸呢。还好看有个插件jquery.jsonp解决了这个问题,好吧再使用jquery.jsonp方法再重写一遍吧。 jquery.jsonp api :https://github.com/jaubourg/jquery-jsonp/blob/master/doc/API.md.

这种的默认回调函数名为:“_jqjsp”。改写好之后,设置timeout短一些之后,还真能检测到timeout错误。心想这总成了吧,把timeout稍微设置长一点,一次粘贴40多张图片,点击保存。报错error错误,不是timeout错误。这是什么原因呢?查看一些浏览器 网络监测。http 400错误,url过长。当初使用$.ajax时就保存失败也不报错,是不是也是url过长的原因呢。url过长能不能用post提交呢,尝试了一把 $.ajax  jsonp格式的提交只能用“get”,不可以用"post"提交。完了做的工作都白做了,一切都归零了。

改用iframe提交吧,iframe可以使用post提交,这样提交的个数就没有限制了。

后台图片保存程序改用多线程保存。对需要下载的图片放到一个dictionary{picpath,‘等待’}中。用for循环遍历,下载每一个图片。由于使用多线程,所以在一个线程判断dictionary中该图片是否下载时,进行锁定。修改该图片的状态“等待”到‘正在下载’。然后放开锁。其他线程就可以判断了,如果状态是“正在下载”,就continue,循环判断下个图片是否下载了。对于用于图片保存分隔的类pi,由于多线程共用,会导致一个图片还没下载完保存,就有新图片加入,导致下载下来的图片不完整。所以把这种共享类放到每个线程中,给每个线程单独分(实例化)一个。在子线程保存的时候根据相对路径判断物理路径是使用了:httpcontent.current.server.mappath()方法,而在子线程中httpcontent.current为null无法判断。后来在传递参数的时候直接传物理地址,而是相对地址。这个转换在主线程中可以完成。  如果用户往富文本中粘贴两边重复的图片,会导致dictionary中键值重复,随在前天页面判断是否有重复的图片地址,如果存在则只在source中存入一份传到后台。当传递了两张重复的图片地址,在返回来替换的时候,如果只是replace,则只会替换一个,第二个符合条件的不进行替换。使用正则表达式则可以解决这个问题。

后台下载保存可能会出现异常,捕获异常,然后返回图片名字为“nanbeiyou”,其他接到反馈进行判断,如果是“nanbeiyou”就知道该图片下载失败,不替换该图片地址。

前天往后台传递用post没有了图片数量限制,但是在后台跳转的时候url长度还是有的限制,随在前台对富文本中图片的数量进行判断,每90张往后台传一次,返回回调,再回调中判断未处理的图片个数,如果为0,就提交整个表单,如果大于90张,则传90张,在0到90之间,则是吧剩余的都传完。

后发现有些网站上的图片url很特殊:以qq空间为例:http://user.qzone.qq.com/307722709/blog/1341022967#!app=2&via=QZ.HashRefresh&pos=1340365654中的一个图片地址:

http://b206.photo.store.qq.com/http_imgload.cgi?/rurl4_b=809f8ca26fba5d947e8642f699ec5571421e5e9c7e38a6cbfdf982c0696537766a8ab54a8f9302ed2743d23408c344b9e9dfeea2928c4f3e0b25cb74c9ebb0da168db30206c32443959287af61818a8321d7dbf5&a=206&b=206

1、该图片地址中没有图片后缀名,通过url分析得不到图片类型。所以在通过reque请求之后,得到

HttpWebResponse response = (HttpWebResponse)request.GetResponse()之后,可以通过response.contentType获得图片的类型,从而得到后缀名。

2.地址中&粘贴到富文本框中之后,对应的隐藏域Tcontent中时&amp;所以需要对该url进行正则匹配将所以的&换成&amp;

3、有的图片地址下载时需要跳转类似于mvc这种,所以将download()中的

request.AllowAutoRedirect = false;这一句去掉。

4、url中存在一个?,在正则中这是一个正则匹配符合,所以需要将该url中?换成\?  在才能正则匹配到正确的url


4、

// $.ajax({
// type:"get",
// timeout:10000,
// async:true,
// url:"@(filePath)/upload.upx?random="+Math.random()+"&pictype=Travel&remotePath="+encodeURI(sources),
// dataType:"jsonp",
// jsonp: "callbackparam",//传递给请求处理程序或页面的,用以获得jsonp回调函数名的参数名(默认为:callback)
// jsonpCallback:"success_jsonpCallback",//自定义的jsonp回调函数名称,默认为jQuery自动生成的随机函数名
// success : function(json){
// $("#Cover").val(json[0].newPath);
// for(var i=0;i<json.length;i++){
// //$("#kecontext>img[src=‘"+json[i].oldPath+"‘]").attr("src",$("#imgpath").val()+json[i].newPath);
// if ($("#imgTitle").val() == "") {
// $("#imgTitle").val(json[i].newPath);
// }
// else {
// $("#imgTitle").val($("#imgTitle").val() + ";" + json[i].newPath);
// }
// $("#Tcontent").val($("#Tcontent").val().replace(json[i].oldPath,$("#imgpath").val()+json[i].newPath));

//
// }
//
// $("#form").submit();
//
// },
// error:function(x,t,m){
// if(t==="timeout")
// {
// alert("您上传的图片太多,保存失败!");
// }else{
// alert("t="+t);
// alert("m="+m);
// alert("x="+x);
// }
//
// }
// });
// $.jsonp({
// type:"post",
// url:"@(filePath)/upload.upx?random="+Math.random()+"&pictype=Travel&remotePath="+encodeURI(sources)+"&callback=?",
// timeout:120000,
// context:sources,
// //callbackParameter:"callbackparam=success_jsonpCallback",
// success:function(json,textStatus){
// $("#Cover").val(json[0].newPath);
// for(var i=0;i<json.length;i++){
//
// if ($("#imgTitle").val() == "") {
// $("#imgTitle").val(json[i].newPath);
// }
// else {
// $("#imgTitle").val($("#imgTitle").val() + ";" + json[i].newPath);
// }
// $("#Tcontent").val($("#Tcontent").val().replace(json[i].oldPath,$("#imgpath").val()+json[i].newPath));

//
// }
// $("#form").submit();
//
// },
// error:function(xOptions, textStatus){
// if(textStatus==="timeout"){
// alert("游记保存超时");
// }
// else{
// alert("发生错误了!");
// }
//
// }
//
// });

string returnVal = "[";
string returnVal = "";

foreach (string path in arr)
{
string oldPath = path;
string oldName = oldPath.Substring(oldPath.LastIndexOf(‘/‘) + 1);
string oldExt = oldPath.Substring(oldPath.LastIndexOf(‘.‘));
MemoryStream ms = DownLoad(oldPath);

string newName = PhotoInfo.GeneratID();
string newExt = "";
Func<string, string> _func = x => x;

using (PhotoInfoChain pi = ThumbFactory.getInstancePhotoinfoChain("Travel"))
{
pi.Func = _func;
pi.Filename = oldName;
pi.Stream = ms;

pi.Save(newName);
ms.Dispose();
newExt = pi.InfoList[0].ImgFormat;
//returnVal +="{oldPath:\""+ oldPath +"\"" +","+ "newPath:" +"\""+ newName + newExt+"\"},";
returnVal += newName + newExt + ":";
}
}
returnVal = returnVal.Substring(0, returnVal.Length - 1);
returnVal += "]";

string callbackFunName = context.Request["callbackparam"];
context.Response.Write(callbackFunName + "(" + returnVal + ")");
context.Response.Write("_jqjsp" + "(" + returnVal + ")");

时间: 2024-10-10 04:18:27

跨域下载图片走的各种弯路和遇到的种种困难的相关文章

canvas图片跨域问题

canvas的drawImage使用跨域图片时候,会报错,解决方法如下: 1. 使用base64替换跨域图片 如果图片不大,且只有几张,可以使用base64,来代替跨域引用图片. 2. 设置image的crossOrigin属性,并且设置服务端的 Access-Control-Allow-Origin:* (或允许的域名) var cvs = document.getElementById('canvas'); var ctx = example.getContext('2d'); var im

GoodReader跨域访问HT for Web手册

最近下载了GoodReader App,发现GoodReader中打开的页面不仅支持WebGL,同时还允许跨域访问资源,以前不少HT for Web手册的例子需要Web服务器发布的方式才能访问,否则需采用<矢量组件设计之道(四)>列举的几种解决跨域访问图片或3D的OBJ等资源的技巧.而现在GoodReader中直接打开就可以玩了,加上GoodReader自身对文档管理的方便性,这1块钱的价格真是物超所值! 以下为GoodReader在iPhone下跨域访问HT for Web手册过程的视频,

Ajax 跨域请求-- Cross Domain

在认识 跨域 之前,先简单了解下域名和协议名,比如下面这个 URL http://mail.163.com/index.html http://              协议名,也就是HTTP超文本传输协议 mail                  服务器名 163.com           域名 mail.163.com    网站名 /                        根目录 index.html         根目录下的默认网页 1.什么是跨域请求? 请求的下一个资源

这些年我们处理过的跨域(转)

出处:http://www.cnblogs.com/humin/p/7136479.html 同源策略 在说跨域之前,我们需要先了解下 同源策略.它是一个规范(Netscape 1995年提出),并没有指定具体的使用范围和实现方式. 为了保证使用者信息的安全,防止恶意网站篡改用户数据,一些常见的Web技术都默认采用了同源策略(如Silverlight, Flash, XMLHttpRequest, Dom等). 那如何判断同源呢? 相同的协议 相同的域名 相同的的端口号 我们用一个表格来展示同源

Flash完美跨域访问的方法

首先,你要确定以下几点,否则可能无法实现: 1.你要跨到哪个域,你必须能管理那域上文件,因为这里要放一个通行文件. 2.你的Flash如果只有SWF,那不一定能实现,因为有时,Flash的AS中,要加入一句话.如果你是跨域调图片.视频一类的,可以用通行文件的方法.通行文件制作方法,请将以下代码存为 crossdomain.xml ,并放到要跨域的目标站点根目录下面.就是说,你的FLASH在 a.com ,你要访问 b.com 上的资源,你就要确定 http://b.com/crossdomain

spring mvc 图片上传,图片压缩、跨域解决、 按天生成目录 ,删除,限制为图片代码等相关配置

spring mvc 图片上传,跨域解决 按天生成目录 ,删除,限制为图片代码,等相关配置 fs.root=data/ #fs.root=/home/dev/fs/ #fs.root=D:/fs/ #fs.domains=182=http://172.16.100.182:18080,localhost=http://localhost:8080 <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE be

spring mvc 图片上传,图片压缩、跨域解决、 按天生成文件夹 ,删除,限制为图片代码等相关配置

spring mvc 图片上传,跨域解决 按天生成文件夹 ,删除,限制为图片代码,等相关配置 fs.root=data/ #fs.root=/home/dev/fs/ #fs.root=D:/fs/ #fs.domains=182=http://172.16.100.182:18080,localhost=http://localhost:8080 <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE b

HTML5笔记:跨域通讯、多线程、本地存储和多图片上传技术

最近做项目在前端我使用了很多新技术,这些技术有bootstrap.angularjs,不过最让我兴奋的还是使用了HTML5的技术,今天我想总结一些HTML5的技术,好记性不如烂笔头,写写文章可以很好的整理思路,写到博客里还能做个备忘. 1) 跨域通讯 现在做企业项目,前端很不自然的会大量使用iframe标签,我以前在文章里提到iframe是一个效率极其低下的标签,但是如果项目没有什么性能的苛求,使用iframe还是非常的方便的. 使用iframe经常碰到父子窗体通讯的问题,我们看看下面的代码:

快速解决Canvas.toDataURL 图片跨域的问题

出现Canvas.toDataURL 图片跨域问题怎么解决呢?下面小编就为大家带来一篇Canvas.toDataURL 图片跨域问题的快速解决方法.一起跟随小编过来看看吧 如题,在将页面的图片地址进行本地输出时(Html2Canvas.js),因不同源存在跨域问题,会出现toDataURL访问权限问题: [Redirect at origin 'http://sub1.xx.com' has been blocked from loading by Cross-Origin Resource S