跨域下載圖片走的各類彎路和遇到的種種困難

  首先聲明 ,我是編程菜鳥,剛作編程不久。歡迎拍磚javascript

  要實現的功能html

     咱們一旅遊網站(南北遊),能夠發表遊記。富文本,寫攻略,上傳圖片(咱們會對圖片進行剪切分紅大中小等各類圖片,方便不一樣地方調用)。java

     但有一種狀況是用戶會把本身發表在其餘網站的文章複製粘貼到咱們網站,結果是咱們只存下了圖片的連接地址,而沒有實際保存圖片。在其餘地方用到該圖片的時候就會出現圖片大小不合適或者顯示掛圖的現象。jquery

     解決的辦法就是根據用戶粘貼的地址,將圖片下載到咱們的服務器上。而後將文本框中其餘網站地址換成本身網站的地址,保存。要實現這個功能可算是費了一番周章。git

  第一次嘗試的辦法:github

       在保存以前 使用jquery對富文本框中的圖片進行遍歷,發現不是咱們網站的圖片就將其地址發到後臺處理,異步提交使用$.post。ajax

           大概的一個代碼正則表達式

$("#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
                })  ;          
            }

                    

     });

  

      後臺根據傳過來的地址,模擬瀏覽器將圖片先保存到內容,而後進行切割保存到服務器上。(很快就實現了,發現要下載的圖片保存到服務器上,着實高興了一下,覺得快完工了呢)編程

  改正1json

      很快發現問題,程序在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

 

一、該圖片地址中沒有圖片後綴名,經過url分析得不到圖片類型。因此在經過reque請求以後,獲得

HttpWebResponse response = (HttpWebResponse)request.GetResponse()
以後,能夠經過response.contentType得到圖片的類型,從而獲得後綴名。

2.地址中&粘貼到富文本框中以後,對應的隱藏域Tcontent中時&amp;因此須要對該url進行正則匹配將因此的&換成&amp;

三、有的圖片地址下載時須要跳轉相似於mvc這種,因此將download()中的  

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

四、url中存在一個?,在正則中這是一個正則匹配符合,因此須要將該url中?換成\?  在才能正則匹配到正確的url



四、

// $.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 + ")");

相關文章
相關標籤/搜索