封装sdk-多图片上传和跨域问题

这是我参与更文挑战的第4天,活动详情查看: 更文挑战

封装企业微信sdk

实现多图片上传

iframe重定向+生成器解决。

为什么要用iframe呢,是因为要解决跨域问题,企业微信接口要求post方法发送消息,所以就直接用了iframe+post来实现跨域(后面讲)。

为什么用生成器,因为生成器不想for循环一样局限于一块作用域,生成器是可以想从哪里取就从哪里取的那种。具体场景:

由于我用form来做跨域,那么当我一个请求发完,form所在的iframe会跳转到请求结果页面,那么我要把它重定向回来当前域,(不然无法发送下一个请求)。那么就一定要等他发完了,页面跳转了,再去重定向回来当前页面。(为啥不能直接写在submit后面,我猜测是因为运行到iframe.src="https://juejin.cn/post/\"这句话的时候还没发生跳转,所以无效,要写在onload里面才行。)既然要重定向,就要上一个锁,否则重定向回来之后就还会经过onload,就还会继续重定向,造成死循环。

以上是背景,那么当我页面重定向回来之后,我还想发送下一个post请求,是不是要判断一下当前发送到哪一个数据了,后面还有没有,如果还有的话就要把锁给打开。

具体流程如下:

  1. 先发送一个form

  2. 页面重定向到请求结果页面

  3. 经过onload,开始判断是否有上锁

    发现没上锁,然后重定向到iframe.src="https://juejin.cn/"

    上锁防止再次重定向

  4. 判断是否有下一个数据

    如果有的话,就继续用form发送

  5. form发送的过程中,要判断是否发送到最后一个了

    不管是发送前面的数据还是发送到最后一个,还是要把锁打开,允许页面再重定向回来一次

    最后一个发送完之后,才能把锁关上

  6. …(重复以上步骤直到数据发送完毕)

也就是说,不仅是iframe内部onload的时候需要判断数据有没有发送完毕,在form发送数据的过程中也需要判断是否发送完毕,如果要用for循环的话,就得把他们两个写到一起,或者用参数传递的方式来判断数据是否发送完毕,有点多余。

先搞一个生成器:

private* getMsg(msg) {
    let i = 0
    while (i < msg.length) {
        yield msg[i];
        i++
    }
    //最后一次发送需要重定向页面
    return "redirect";
}
复制代码

跨域问题解决

利用form+iframe

先封装一个iframe:

//防止iframe重定向递归
let lock = true
// 控制iframe并利用form发送数据
public IframePost(msg, format = ""): Promise<any>{
    // 只需要一个iframe
    let iframe = window.frames["postFrame"]?.frameElement
    // 如果是数组,说明是多图片上传,需要用到生成器
    this.genMsg = Array.isArray(msg)?this.getMsg(msg):null
    if(!iframe){
        iframe = document.createElement('iframe')
        iframe.style.display = "none";
        iframe.name = "postFrame"
        iframe.id = "postFrame"
        // 监听onload事件,页面重新加载时触发
        iframe.addEventListener('load', ()=>{
            // 如果没有上锁就可以重定向页面
            if(!lock){
                // 是否重定向页面
                // 重定向流程 当前页面->提交数据之后跑到后端数据页面->后端页面onload时重定向到当前页面
                // 如果不重定向:停留在后端数据页面,可以看到返回数据,但会由于跨域无法再次发送请求
                iframe.src = "/"
                // 这里重定向回来之后要上一次锁,否则会造成重定向死循环
                lock = true
                // 如果生成器有值,继续发送下一个信息
                if(this.genMsg){
                    let nextMsg = this.genMsg.next()
                    // 利用form发送数据
                    // nextmsg发送到最后一个的时候,这里到底是怎么通过的?
                    !nextMsg.done&&this.FormPost(nextMsg, format)
                }
            }
        })
        document.body.appendChild(iframe)
    }
    if(this.genMsg){
        //如果是数组(图片和文件),就利用生成器获取发送消息
        return this.FormPost(this.genMsg.next(), format)
    }else{
        return this.FormPost(msg, format)
    }
}      
复制代码

封装form:

// 利用创建form发送数据
private FormPost(msg, format = ""){
    const form = document.createElement("form");
    form.style.display = "none";
    // 把form跳转的页面放到框架里,这样写的好处是可以直接从windows下拿到form而不需要放到iframe里面去
    form.target = "postFrame"
    form.id = "postForm"
    return new Promise((resolve, reject) => {
        form.setAttribute("method", "POST");
        // 定义form的格式(消息体的格式)
        this.getFormatForm(form, msg, format)
        document.body.appendChild(form);
        //提交表单
        form.submit();
        resolve(form)
    }).then((resolve)=>{
        //提交完就删除
        form.remove()
        //如果还需要一次重定向就不上锁
        if(msg.value === "redirect"){
            lock = false
        }else{
            //不上锁,直到发送消息结束的时候上锁
            lock = msg.done
        }
    })
}
复制代码
  1. 发送application/x-www-form-urlencoded格式数据

    默认就是这个格式,所以不需要多设置什么。

    form.setAttribute("action", "http://localhost:3000/efoxPayWechatTips");
    let bodyKey = [
        {
            name: "rootKey",
            value: this.key
        },
        {
            name: "sendMsg",
            value: this.genMsg?msg.value:msg
        }
    ]
    bodyKey.forEach((el) => {
        var input = document.createElement("input");
        input.name = el.name;
        input.value = JSON.stringify(el.value);
        form.appendChild(input);
    })
    复制代码
  2. 发送formdata格式数据

    // 如果要发送formdata类型就直接把页面上的input赋值到form的input中
    form.setAttribute("action", "http://localhost:3000/postFile");
    form.setAttribute("enctype","multipart/form-data")
    let input = document.createElement("input");
    // 当使用formdata的时候,msg其实是一个input,直接把该input放到表单中
    input = msg;
    form.appendChild(input);
    let keyInput = document.createElement("input");
    keyInput.name = "rootKey"
    keyInput.value = this.key
    form.appendChild(keyInput);
    复制代码
© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享