canvas教程

iframe实现跨域post请求的技术细节

字号+ 作者:H5之家 来源:H5之家 2017-08-19 10:04 我要评论( )

在最近的一个项目中,我打算在页面上实现这样一个功能: 在网页上画出某种图形,上传到服务器后,返回一个src地址。这个地址可以用来分享到各种社交媒体。 这个功能看似非常简单,但要实现它还需要注意各种小的细节。 首先说下思路和技术要点: 用canvas来实

在最近的一个项目中,我打算在页面上实现这样一个功能:
在网页上画出某种图形,上传到服务器后,返回一个src地址。这个地址可以用来分享到各种社交媒体。

这个功能看似非常简单,但要实现它还需要注意各种小的细节。

首先说下思路和技术要点:

  • 用canvas来实现页面的画图功能,利用canvas的toDataURL方法可以很方便地将画布的数据保存为dataURIs。
  • 用jQuery的ajax方法将图像数据异步提交到后台,在后台处理上传后,返回一个地址给前端调用。
  • 按照这个思路马上就用jQuery.get()实现了一个测试用例。但结果非常让人失望!下面就是我遇到的各种问题:

    1. post OR get

    首先是请求方式问题。

    我们知道,http的get请求是利用url发送给服务器请求,受制于字节数限制,无法发送大的数据量。jquery中的get方法是其ajax方法的一个封装,基本原理仍是利用XMLHttpRequest对象发送了一个get请求,要发送图片这种较大的数据,用get方式显然力不从心,所以必须用post方式提交。

    心想这么简单,于是把get改成post,over!
    但结果失败了,因为这里遇到了另一个很严重的问题。

    2. 跨域提交(如果你是同域上传的话,这一段可以忽略了)

    jQuery中的post方法在同域的情况下,可以顺利地以异步的方式提交form 数据,并用返回一个json结果。
    所以,在同域的情况下,你可以这样
    $.post("url",form1.serialize(),function(json){console.log(json)},'json');
    服务器将处理后的结果用json形式返回给前端。

    但在我的工作环境中,跨域是很常见的。而且我也希望能将这个功能组件化,让它在不同的子域中调用。
    这样就必须要用到跨域的方式,于是很自然地想到jQuery中的jsonp。而且在jquery官方的ajax方法文档中,写明了dataType的值可以是"jsonp"。
    于是又去改了一通,点了该死的按钮以后返回的是"HTTP 414 Request-URI too long"!God!
    原来jQuery的post方法在跨域提交时会自动转换为GET方式,此时提交的数据已经超过了GET方式的请求字节限制!

    这个时候,虽然有些气急败坏,但终于找到症结所在,就是下面要说的问题:

    3. 如何在跨域的情况下异步发送post请求?

    这里要说明一个情况,就是为什么要用异步?
    因为在我之前的许多项目中,大多都是在页面上发送一个异步请求,把服务器的结果实时地展示到页面上来,而不必刷新整个页面。当然会很自然地想到这样的实践方式。

    我没有考虑到的是,之前的这些实践都是发送的get请求。当发送get请求时,如果是跨域,利用XMLHttpRequest对象发送get请求时可以用jsonp的方式跟服务器配合取回结果;如果是同域,那就更没有问题了。

    但这次要上传图像数据,只能发送post请求,而且要异步实现。这可就犯难了。
    好在守着互联网这个宝库,稍微搜索了一番以后,终于发现了一些有用的东西。

    首先是这篇:POST跨域问题

    看了这个后,知道了异步的情况下是不能跨域发送post请求的。想想也是,你在自己的网站上随便form发送了一大段数据到别人的网站,人家不搭理你就不错了,还要人家给你返回个“哥俩好”,可能么?

    安全很重要,但我自己的两个子域虽然是跨域,但总归是一家子。一家人怎么才能不说两家话呢?

    来看这个:跨域post请求实现方案小结
    文档中列举出了目前实现跨域请求很多方法,如CORS、invisible iframe、server proxy、flash proxy。

    还有一些文章列出了HTML5 WebSocket方法,但这种方式目前还不是标准,或者还没有流行起来,要想让功能以比较稳定的方式运行,最好还是用当下主流的方式。于是决定用iframe来实现。

    4. iframe实现跨域提交的原理


    iframe进行post“跨域”无刷新提交原理

    看了这个图以后,基本上思路就明晰了。

    我们用异步的目的是实现无刷新,既然post不能跨域异步提交,并且不异步也可以无刷新,那我们就不异步。我把form提交到本页面的一个空iframe中,这样不会造成页面的刷新。让服务器处理完后跳转到同域下的upload_result网页里,并给这个网页的url附加处理的结果。这样原始请求页的空iframe就加载了服务器跳转的那个页面的内容。同时,虽然这个upload_result页虽然跟我的原始页面不在一个子域,但我可以人为地设置它们的document.domain为同一个父域,当这个upload_result页面在我的原始请求页加载时,就可以执行父页面的callback函数。

    5. 实现步骤

    明白了基本的原理,剩下的就是体力活了,下面看看怎么构建这样一个机制。

  • 首先在服务器端写好一个upload_result.html,里面用js解析出页面URL附带的信息。其中包括两个最重要的信息,服务器上传图片后的地址和原始页面请求的回调函数名
  • 在服务器端写好upload脚本,处理前端post过来的数据,最重要的两个域对应地是文件信息(这里是二进制数据)和回调函数名称。当处理完成图片上传后,用header("Location: upload_result.html?paramString")跳转到upload_result页。其中paramString包含了url格式的图像地址和回调函数名称键值对。
    需要注意的是,传给服务器端的数据是base64编码的,需要先解码才能保存。
  • 前端页面需要构造的元素如下:
  • 这样,当用户在canvas画完图,点击upload按钮时,把canvas的信息和回调函数名赋值给form,触发form提交。结果返回给空iframe,iframe 解析自身的返回结果,并触发父页面的callback函数。于是实现了页面的无刷新跨域post提交。Good!

    参考:

    延伸

    实现了canvas二进制图像的跨域上传和返回,那么在此基础上还可以做些更好玩的:比如图像编辑。
    功能很简单:用户打开本地的图片文件并加载到canvas中,在画布上进行各种编辑,完了上传到服务器,并把返回的地址分享出去。
    这里要用到的一个新东西就是HTML5的FileReader对象。利用它可以实现如本地预览等功能,我们用它来把选中的本地文件加载到canvas中,编辑后上传。

     

    1.本站遵循行业规范,任何转载的稿件都会明确标注作者和来源;2.本站的原创文章,请转载时务必注明文章作者和来源,不尊重原创的行为我们将追究责任;3.作者投稿可能会经我们编辑修改或补充。

    相关文章
    • canvas实现百度地图个性化底图绘制

      canvas实现百度地图个性化底图绘制

      2017-08-18 13:03

    • Python实现的弹球小游戏示例

      Python实现的弹球小游戏示例

      2017-08-17 15:02

    • python学习笔记14--用Tkinter实现GUI编程

      python学习笔记14--用Tkinter实现GUI编程

      2017-08-15 17:00

    • js Canvas实现圆形时钟教程

      js Canvas实现圆形时钟教程

      2017-08-15 13:03

    网友点评
    a