zoukankan      html  css  js  c++  java
  • 无刷新跨域上传的回调处理解决方案

    最近要做一个基于WebApi接口的图片无刷新上传,开始没在意,上传图片嘛,分分钟的事。

    结果,图片传上去,的确是分分钟就解决了,但要回调页面的js给img标签赋值,却出现了问题。

    原因很容易搞清楚:无刷新上传必须用一个隐藏的iframe来响应上传请求,而这个请求回来的结果,跟当前页不在一个域里面,也就是跨域了。

    而总所周知的是,现在的浏览器,对安全规范的实现是越来越严格了,没有任何浏览器支持这种跨域的页面访问。

    因此,api里返回的回调js根本访问不到iframe外面去。

    经过近两个小时的艰苦卓绝的查证工作,阅读了许多国内外资料,没有一个好的方法,最终都是用专门的插件来完成,比如flash上传等,而我是特别讨厌用插件的。

    偶然在国外一篇文章的评论里,有个小伙子随便回了一句,说可以考虑把跨域转为本站,他虽然是随便一说,也没说怎么个转法,转什么,

    却给了我一个提示,那就是可以转iframe里的js为本站js。

    稍微想下,就觉得这个可行,于是立即动手:

    1>先在本站建立一个空的静态页,命名为UploadCallback.html,写入下面的内容

    <!DOCTYPE html>
    <html xmlns="http://www.w3.org/1999/xhtml">
    <head>
        <title>test1</title>
        <script type='text/javascript'>
            var url = location.href; //取得整个地址栏
            var ps = url.split("?")
            var js = ps.length > 0 ? ps[1] : '';
            //alert(decodeURI(js));
            eval(decodeURI(js));
        </script>
    </head>
    <body></body>
    </html>
    

    2>上传表单代码如下:

    <div class="edit-area">
        <iframe id="uploadFrame" name="uploadFrame" style="display:none;"></iframe>
        <div class="edit-line" style="line-height:60px;">
            <img class="edit-image left" alt="" src="" id="img_a" />
            <div class="edit-title left"></div>
            <form id="form_a" class="edit-form right" method="post" target="uploadFrame" enctype="multipart/form-data" action="http://192.168.2.112:89/Files/Post">
                <input type="hidden" name="ReturnUrl" value="http://192.168.2.111:81/UploadCallback.html" />
                <input type="file" name="file_a" onchange="this.parentNode.submit();" />
            </form>
        </div>
    </div>
    <script type="text/javascript">
        function UploadCallback(result) {
            console.log(result);
            $('.edit-image').attr('src', result.Url);
        }
    </script>


    3>api接收方法代码:(考虑到返回内容的管理,这里没有用标准WebApi 协议,而是用的MVC的Action来模拟)

    public class FilesController : Controller
        {
            //要存放的文件虚拟路径
            private string virtualPath = ConfigurationManager.AppSettings["VirtualImagePath"].ToString();
            private string physicPath = ConfigurationManager.AppSettings["PhysicImagePath"].ToString();
    
            [HttpPost]
            public ContentResult Upload()
            {
                //要返回的json对象
                List<FilesModels> list = new List<FilesModels>();
                var re = "<script type = 'text/javascript'>location.href="" + Request["ReturnUrl"] + "?\"window.parent.UploadCallback([";
                try
                {
                    //循环遍历上传文件
                    for (int i = 0; i < Request.Files.Count; i++)
                    {
                        //获取当前时间戳作为文件名
                        string fileName = Tools.GetTimeStamp();
                        //获取文件后缀名
                        var file = Request.Files[i];
                        var name = file.FileName;
                        string fileSsuffix = name.Substring(name.LastIndexOf('.'));
                        //上传
                        file.SaveAs(physicsPath + fileName + fileSsuffix);
                        re += "{'key': '" + Request.Files.AllKeys[i] + "','url': '" + virtualPath + fileName + fileSsuffix + "','len': '" + file.ContentLength.ToString() + "','status': " + Response.StatusCode + ",'message': 'Upload Success'},";
                    }
                }
                catch
                {
                    re += "{'status': '" + Response.StatusCode + "', 'message': 'Upload Failed'}";
                }
                re += "]);\""</script>";
                return Content(re);
            }
        }

    代码完毕,现在解释下运行原理:

    其实很简单,关键在于api返回的js是一个跳转语句,

    把当前iframe的页面从api的域跳转到表单指定的ReturnUrl,

    也就是前面定义的UploadCallback.html,

    并且把真正要执行的回调方法作为参数带过去,

    然后这个页面用js对当前url进行截取,获取后面追加的js代码,并用eval执行。

    几个注意点:

    1>表单提交时要把指定的回调页面的url作为提交项,提交上去

    2>api返回的内容要特别注意拼写,因为现在的浏览器都直接禁止了url含有可执行js脚本,因此一定是转成字符串拼接

    3>回调页面js截取需要解码并用eval方法执行,因为url取下来的是经过转码的字符串


    至此,这个困扰世界的麻烦事,就这么简单的解决了^_^





  • 相关阅读:
    全代码实现ios-4
    集训第一次周赛题目及题解
    网站登录时密码忘记,通过向邮箱发送验证链接实现重置密码的实现方法
    hdu 1861-游船出租
    c#获取或修改配置文件
    今天做php经典实例,发现,我是对的,面试官给我说错了
    HDU 4637 Rain on your Fat brother 线段与半圆和线段交 简单题
    Qt之图标切分与合并
    标准容器的共性及举例
    如何提高数据库update更新的速度
  • 原文地址:https://www.cnblogs.com/foren/p/6009080.html
Copyright © 2011-2022 走看看