孙肖宁 发布的文章

小编今天在利用Arduino开发工具开发esp8266的程序的时候,发现无法写入,出现这样的错误

问题:
Executable segment sizes:
IROM   : 243180          - code in flash         (default or ICACHE_FLASH_ATTR)
IRAM   : 26888   / 32768 - code in IRAM          (ICACHE_RAM_ATTR, ISRs...)
DATA   : 1264  )         - initialized variables (global, static) in RAM/HEAP
RODATA : 964   ) / 81920 - constants             (global, static) in RAM/HEAP
BSS    : 24968 )         - zeroed variables      (global, static) in RAM/HEAP
项目使用了 272296 字节,占用了 (26%) 程序存储空间。最大为 1044464 字节。
全局变量使用了27196字节,(33%)的动态内存,余留54724字节局部变量。最大为81920字节。
pyserial or esptool directories not found next to this upload.py tool.

解决办法:
打开 arduino esp8266 sdk 目录: ~/Library/Arduino15/packages/esp8266/hardware/esp8266/2.7.4/tools/
打开浏览器下载最新版 esptool 和 pyserial
下载地址:

https://github.com/espressif/esptool/archive/v3.0.zip
https://github.com/pyserial/pyserial/archive/v3.4.zip

解压后,把文件夹重命名为esptool和pyserial,然后直接替换就可以了。

飞鹅标签机是支持打印图片的,不过只能识别黑色,灰色居然给忽略了,毕竟是热敏打印机,只能打印黑色,不知道是不是刚出来的原因,无论是官方的文档,还是提供的DEMO,都不是很完善,特别是打印图片的,只提供了PHP的DEMO。可惜小编是用Python写的,下面小编分享下,Python的案例:
注意:官方这么要求图片的:图片二进制数据,需配合标签使用,最佳效果为不大于224px的正方形(宽高都为8的倍数)黑白图,支持jpg、png、bmp,不能超过10K。 小编提醒大家:图片一定要用正方形的。

#coding:utf-8

import time
import hashlib
import requests
import io
from requests_toolbelt import MultipartEncoder

def printLabelMsg(qr_addredd, serial):
    # 初始化标签大小
    # content = '<SIZE>60,40</SIZE>'
    content = ""
    content += '<IMG x="200" y="30">'
    content += '<TEXT x="280" y="55" font="12" w="2" h="2" r="0">打印图片</TEXT>'
    STIME = str(int(time.time()))#不需要修改
    LOGO = "本地图片路径"
    params = {
        'user':******,
        'stime':*******,
        'sig':signature(STIME),
        'apiname':'Open_printLabelMsg',
        'sn':******,
        'content':content,
        'times': TIMES,
        'img': (LOGO, open(LOGO, 'rb'), 'application/jpeg')
    }
    m = MultipartEncoder(params)
    response = requests.post('http://api.feieyun.cn/Api/Open/', data=m,headers={'Content-Type': m.content_type}, timeout=30)
    code = response.status_code #响应状态码
    if code==200:
        con_str=str(response.content, encoding = "utf-8")  
        content=eval(con_str)
        if(content.get('ret')!=0):
            raise RuntimeError(con_str)
        print(response.content)#服务器返回的JSON字符串,建议要当做日志记录起来
    else:
        raise RuntimeError('code{}'.format(code))

下面小编提供下官方客服给的PHP的案例:

<?php


function printLabelMsg($user, $skey, $sn, $content, $times = 1)
{
    $url = 'http://api.feieyun.cn/Api/Open/';
    $stime = time();
    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, $url);
    curl_setopt($ch, CURLOPT_TIMEOUT, 30);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
    curl_setopt($ch, CURLOPT_POST, 1);
    curl_setopt($ch, CURLOPT_POSTFIELDS, [
        'user' => $user,
        'stime' => $stime,
        'sig' => sha1($user.$skey.$stime),
        'apiname' => 'Open_printLabelMsg',
        'sn' => $sn,
        'content' => $content,//这里就是配合使用的IMG标签
        'times' => $times,
        'img' => new \CURLFile('test.jpg')
    ]);
    $headers = array('Content-Type: multipart/form-data;');//传图
     curl_setopt($ch,CURLOPT_HTTPHEADER,$headers);
    $response = curl_exec($ch);
    curl_close($ch);

    var_dump( $response );
}

printLabelMsg('*************', '*******************', '**********', '<img x="50" y="10">');//USER,UKEY,打印机编号

做过爬虫的小伙伴应该都很清楚要伪装自己的请求,包括请求头等,同时也要隐藏自己的ip,不然的话很快自己的ip就被封了,访问不了了。免费的代理ip很多,也有收费的,要是你的资金允许,或者对ip要求比较高的话,建议直接去购买收费的代理ip,直接通过接口就能获取。小编在这里主要是说下爬取的快代理的免费ip的部分,并且验证他对自己要访问的网站是否可以访问,并且保存到redis或者文件当中。
我们打开快代理的网站,免费代理的页面https://www.kuaidaili.com/free/inha/1/
好了,下面下面分享下爬取的代码吧

import requests
from lxml import etree
from fake_useragent import UserAgent
import redis
import threading
import time


pool = redis.ConnectionPool(host='127.0.0.1', port=6379, db=1, password='')

# 测试的URL
test_url = "https://www.1688.com/"

def get_proxy(url):
    '''爬取快代理的免费代理ip'''
    headers= {'User-Agent':str(UserAgent(verify_ssl=False).random)}
    response = requests.get(url,headers=headers)
    selector = etree.HTML(response.text)
    proxies = []
    for each in selector.xpath('//table[@class="table table-bordered table-striped"]/tbody/tr')[1:]:
        ip = each.xpath("./td[1]/text()")[0]
        port = each.xpath("./td[2]/text()")[0]
        proxy = ip + ":" + port
        proxies.append(proxy)
    test_proxies(proxies, test_url, 'ip_proxy')

def test_proxies(proxies, test_url , ipname):
    '''检查IP是否可以使用'''
    proxies = proxies
    normal_proxies = []
    count = 1
    for proxy in proxies:
        print("第{}个, {}".format(count, proxy))
        count += 1
        try:
            headers= {'User-Agent':str(UserAgent(verify_ssl=False).random)}
            response = requests.get(test_url, headers=headers, proxies={"http": proxy}, timeout=1)
            time = response.elapsed.total_seconds()
            if response.status_code == 200:
                print("{}可以使用".format(proxy))
                normal_proxies.append(proxy)
            else:
                print("{}--{}--可以不可以访问".format(proxy, response.status_code))
        except Exception as e:
            print("{}无效".format(proxy))
    write_proxy(normal_proxies, ipname)

def write_proxy(proxies, name):
    '''写入文件或redis'''
    for proxy in proxies:
        # 写入redis
        r = redis.Redis(connection_pool=pool)
        r.lpush(name, proxy)
        print(r.lrange(name, 0, -1))
        # 写入文件
        with open("./{}.txt".format(name), 'a+') as f:
            f.write(proxy + '\n')
    print("录入完成!!!")

if __name__ == "__main__":
    base_url = "https://www.kuaidaili.com/free/inha/{}/"
    for i in range(1, 3000):
        print("第{}页".format(i))
        url = base_url.format(i)
        get_proxy(url)

5.jpg

不知道大家有没有这样的经历,换服务器的时候,网站的文件,数据库都得先备份,下载下来,然后再上传到新的服务器,重新进行配置,很是麻烦,所以不要轻易换服务器。今天在帮朋友迁移网站的时候,发现宝塔面板里面提供了迁移工具的宝塔一键迁移API版本。不知道为啥加API,反正就是好用就行了。下面小编说下使用方法以及注意事项:

环境准备:

两台服务器上都要安装宝塔面板,这个小编两个服务器的宝塔版本都是最新的(7.5.1)。还是建议大家升级到最新版本。
迁出的服务器: 安装宝塔一键迁移API版本,在软件商店里面安装就可以。(官方说:安装之前在首页点击一下修复)。
迁入的服务器: 只要保证面板版本必需>=6.9.5就可以了。

开始迁移

  • 迁入机器: 打开面本的API,点击面板设置-->API接口,获取接口API,并且吧迁出机器的ip放到白名单里面
    1.png
  • 迁出机器: 点击宝塔一键迁移API版本里面的设置,输入迁入机器的ip和刚刚获取的API。
    3.png
  • 检查环境,如果两个机器环境不一样,迁入机器缺什么都会提示的,按照提示装上就可以了。
    4.png
  • 选择要迁移的数据库,FTP,和网站就可以了。
    5.png
  • 接下来就是等待了
    6.png

注意: 迁出机器要留足够的空间,因为网站文件是要压缩的,不然没办法迁移。
努力

小编今天在使用npm install安装东西的时候出现了这样的错误

gyp: No Xcode or CLT version detected!
gyp ERR! configure error
gyp ERR! stack Error: `gyp` failed with exit code: 1
gyp ERR! stack     at ChildProcess.onCpExit (/usr/local/lib/node_modules/npm/node_modules/node-gyp/lib/configure.js:351:16)
gyp ERR! stack     at ChildProcess.emit (events.js:315:20)
gyp ERR! stack     at Process.ChildProcess._handle.onexit (internal/child_process.js:277:12)
gyp ERR! System Darwin 20.3.0
gyp ERR! command "/usr/local/bin/node" "/usr/local/lib/node_modules/npm/node_modules/node-gyp/bin/node-gyp.js" "rebuild"
gyp ERR! cwd /Users/zero/projects/test/vue_test/big-screen-vue-datav/node_modules/fsevents
gyp ERR! node -v v14.16.0
gyp ERR! node-gyp -v v5.1.0
gyp ERR! not ok

看上面的提示可能是Xcode的问题,那么我们就重装下Xcode的吧,果然解决了问题。

xcode-select --install

ThinkPHP实现微信分享好友转发朋友圈自定义图片和文字的方法,在开始之前呢,我们需要准备好一些东西:

前期准备

  • 认证的公众号,订阅号或者服务号都可以,只要是认证过的就可以。
  • 公众号的AppID和AppSecret,登录微信公众平台,开发—基本配置,就可以看到啦。
  • 设置JSJS接口安全域名,登录微信公众平台,设置—公众号设置—功能设置里,填写就可以了。
  • 官方SDK,我们直接下载案例就可以,下载地址:http://demo.open.weixin.qq.com/jssdk/sample.zip里面包含php、java、nodejs以及python的示例代码。

服务端准备(ThinkPHP)

  • 将刚刚下载的jssdk.php文件重命名为Jssdk.php,然后和access_token.php、jsapi_ticket.php一起放入到ThinkPHP框架的第三方接口扩展目录下(extend/org/wechat/)。
  • 修改Jssdk.php文件

    1. 首先我们需要在构造函数中设置 $this->path = __DIR__ . DS; 即:

      namespace wechat;
      class Jssdk  {
       private $appId;
       private $appSecret;
       private $path;
       public function __construct($appId, $appSecret) {
       $this->appId = $appId;
       $this->appSecret = $appSecret;
       $this->path = __DIR__ . DS;
       }
       ...
      }
    2. get_php_file函数返回值中的$filename改为 $this->path.$filename ,即:

      private function get_php_file($filename) {
        return trim(substr(file_get_contents($this->path.$filename), 15));
      }
    3. 设置token的缓存,修改getAccessToken,加入cache

      private function getAccessToken() {
        // access_token 应该全局存储与更新,以下代码以写入到文件中做示例
        // cache('at', ['access_token'=>'sss', 'expire_time' => '123']);
        // $data = json_decode($this->get_php_file("access_token.php"));
        $data = cache('at');
        if ($data['expire_time'] < time()) {
       // 如果是企业号用以下URL获取access_token
       // $url = "https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid=$this->appId&corpsecret=$this->appSecret";
       $url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=$this->appId&secret=$this->appSecret";
       $res = json_decode($this->httpGet($url));
       $access_token = $res->access_token;
       if ($access_token) {
         // $data->expire_time = time() + 7000;
         // $data->access_token = $access_token;
         cache('at', ['access_token'=>$access_token, 'expire_time' => time() + 7000]);
         // $this->set_php_file("access_token.php", json_encode($data));
       }
        } else {
       $access_token = $data['access_token'];
        }
        return $access_token;
      }
  • 在控制器里面调用

    1. 导入jssdk, use wechat\Jssdk;
    2. 传给前端

      $jssdk = new Jssdk($AppID, $AppSecret);
      $res = $jssdk->getSignPackage();
      $appId = $res['appId'];
      $timestamp = $res['timestamp'];
      $nonceStr = $res['nonceStr'];
      $signature = $res['signature'];
      $this->assign(
       array(
           'appId'=>$appId,
           'timestamp'=>$timestamp,
           'nonceStr'=>$nonceStr,
           'signature'=>$signature,
       )
      );

      网页端

  • 导入jssdk

    <script src="http://res.wx.qq.com/open/js/jweixin-1.2.0.js"></script>
    
  • 设置分享内容

    <script type="text/javascript">
      var title = '分享标题';
      var imgUrl = '分享图片';
      var link = '分享地址';
      var desc = '分享描述';
     
      wx.config({
          debug: false,//开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。
          appId: '{$appId}', // 必填,公众号的唯一标识
          timestamp: '{$timestamp}', // 必填,生成签名的时间戳
          nonceStr: '{$nonceStr}', //必填, 生成签名的随机串
          signature: '{$signature}', //必填,签名
          jsApiList: ['onMenuShareTimeline', 'onMenuShareAppMessage', 'onMenuShareQQ', 'onMenuShareQZone'] //必填, JS接口列表,这里只填写了分享需要的接口
      })
      wx.ready(function () {
          wx.onMenuShareTimeline({
              title: title,
              link: link,
              desc:desc,
              imgUrl: imgUrl,
              success: function() {
                  // 用户确认分享后执行的回调函数
              },
              cancel: function() {
                  // 用户取消分享后执行的回调函数
              }
          });
          wx.onMenuShareAppMessage({
              title: title, // 分享标题
              desc: desc, // 分享描述
              link: link, // 分享链接
              imgUrl: imgUrl, // 分享图标
              type: 'link', // 分享类型,music、video或link,不填默认为link
              dataUrl: '', // 如果type是music或video,则要提供数据链接,默认为空
              success: function() {
                  // 用户确认分享后执行的回调函数
              },
              cancel: function() {
                  // 用户取消分享后执行的回调函数
              }
          });
      })
    </script>

做前端的小伙伴应该都很清楚使用vue的v-for就可以很方便的一个数组,然后对数组中的元素就行展示或者操作,那么有没有考虑过遍历对象呢。我们在学习js的时候都应该知道,我们使用js是可以遍历对象的,比如:

var oj = {"a":1, "b": 2, "c": 3}
for(var i in oj){
    console.log(i,"----->" ,oj[i])
}
// 输出
// a -----> 1
// b -----> 2
// c -----> 3

其实使用vue的v-for的效果是一样的,也是用的in。下面案例的输出结果是一样的:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
</head>
<body>
    <div id="app">
        <p v-for="(val, key, index) in list">{{key}}----->{{val}}</p>
    </div>

    <script>
        var obj=
        new Vue({
            el: '#app',
            data:{
                list:{
                    'a':1,
                    'b':2,
                    'c':3
                }
            }
        })
    </script>
</body>
</html>

我们在使用微信小程序开发工具进行测试的时候发现使用chooseVideo或者是chooseMedia的时候,会出现"chooseVideo:fail DEMUXER_ERROR_NO_SUPPORTED_STREAMS: FFmpegDemuxer: no supported streams"这样的错误,看大家在社区已经反映过了,官方给出的答复是正在修复,所以现在也没有比较好的解决方案。我们可以使用老版本进行测试,下面小编就给大家提供下小编使用的版本,这个版本的缺陷就是,在MacOS Big Sur出现频繁崩溃的问题,也不是十分好用。

系统:MacOS Big Sur
工具版本: 微信开发者工具1.03.2011120
问题: chooseVideo或者是chooseMedia,出现"chooseVideo:fail DEMUXER_ERROR_NO_SUPPORTED_STREAMS: FFmpegDemuxer: no supported streams"

下载地址: https://developers.weixin.qq.com/community/develop/doc/0000c4f9440410caa11ab51cd5b801

今天在进行测试的时候,发现前端后端签名经常不一致,而且是在参数里面含有中文的时候才会出现这样的问题。为了排查问题,小编又找了几个第三方的md5加密工具,对需要签名的字符串进行加密,经过比较发现是前端出现了问题,后来小编又重新找了个加密文件,在这里跟大家分享下。具体的原理小编也没有深究,好像是需要将中文先转为utf-8。最后几行是在es6中导出使用的,如果需要自己解开注释。
源代码:

function md5(string) {
    function md5_RotateLeft(lValue, iShiftBits) {
        return (lValue << iShiftBits) | (lValue >>> (32 - iShiftBits));
    }

    function md5_AddUnsigned(lX, lY) {
        var lX4, lY4, lX8, lY8, lResult;
        lX8 = (lX & 0x80000000);
        lY8 = (lY & 0x80000000);
        lX4 = (lX & 0x40000000);
        lY4 = (lY & 0x40000000);
        lResult = (lX & 0x3FFFFFFF) + (lY & 0x3FFFFFFF);
        if (lX4 & lY4) {
            return (lResult ^ 0x80000000 ^ lX8 ^ lY8);
        }
        if (lX4 | lY4) {
            if (lResult & 0x40000000) {
                return (lResult ^ 0xC0000000 ^ lX8 ^ lY8);
            } else {
                return (lResult ^ 0x40000000 ^ lX8 ^ lY8);
            }
        } else {
            return (lResult ^ lX8 ^ lY8);
        }
    }

    function md5_F(x, y, z) {
        return (x & y) | ((~x) & z);
    }

    function md5_G(x, y, z) {
        return (x & z) | (y & (~z));
    }

    function md5_H(x, y, z) {
        return (x ^ y ^ z);
    }

    function md5_I(x, y, z) {
        return (y ^ (x | (~z)));
    }

    function md5_FF(a, b, c, d, x, s, ac) {
        a = md5_AddUnsigned(a, md5_AddUnsigned(md5_AddUnsigned(md5_F(b, c, d), x), ac));
        return md5_AddUnsigned(md5_RotateLeft(a, s), b);
    };

    function md5_GG(a, b, c, d, x, s, ac) {
        a = md5_AddUnsigned(a, md5_AddUnsigned(md5_AddUnsigned(md5_G(b, c, d), x), ac));
        return md5_AddUnsigned(md5_RotateLeft(a, s), b);
    };

    function md5_HH(a, b, c, d, x, s, ac) {
        a = md5_AddUnsigned(a, md5_AddUnsigned(md5_AddUnsigned(md5_H(b, c, d), x), ac));
        return md5_AddUnsigned(md5_RotateLeft(a, s), b);
    };

    function md5_II(a, b, c, d, x, s, ac) {
        a = md5_AddUnsigned(a, md5_AddUnsigned(md5_AddUnsigned(md5_I(b, c, d), x), ac));
        return md5_AddUnsigned(md5_RotateLeft(a, s), b);
    };

    function md5_ConvertToWordArray(string) {
        var lWordCount;
        var lMessageLength = string.length;
        var lNumberOfWords_temp1 = lMessageLength + 8;
        var lNumberOfWords_temp2 = (lNumberOfWords_temp1 - (lNumberOfWords_temp1 % 64)) / 64;
        var lNumberOfWords = (lNumberOfWords_temp2 + 1) * 16;
        var lWordArray = Array(lNumberOfWords - 1);
        var lBytePosition = 0;
        var lByteCount = 0;
        while (lByteCount < lMessageLength) {
            lWordCount = (lByteCount - (lByteCount % 4)) / 4;
            lBytePosition = (lByteCount % 4) * 8;
            lWordArray[lWordCount] = (lWordArray[lWordCount] | (string.charCodeAt(lByteCount) << lBytePosition));
            lByteCount++;
        }
        lWordCount = (lByteCount - (lByteCount % 4)) / 4;
        lBytePosition = (lByteCount % 4) * 8;
        lWordArray[lWordCount] = lWordArray[lWordCount] | (0x80 << lBytePosition);
        lWordArray[lNumberOfWords - 2] = lMessageLength << 3;
        lWordArray[lNumberOfWords - 1] = lMessageLength >>> 29;
        return lWordArray;
    };

    function md5_WordToHex(lValue) {
        var WordToHexValue = "",
            WordToHexValue_temp = "",
            lByte, lCount;
        for (lCount = 0; lCount <= 3; lCount++) {
            lByte = (lValue >>> (lCount * 8)) & 255;
            WordToHexValue_temp = "0" + lByte.toString(16);
            WordToHexValue = WordToHexValue + WordToHexValue_temp.substr(WordToHexValue_temp.length - 2, 2);
        }
        return WordToHexValue;
    };

    function md5_Utf8Encode(string) {
        string = string.replace(/\r\n/g, "\n");
        var utftext = "";
        for (var n = 0; n < string.length; n++) {
            var c = string.charCodeAt(n);
            if (c < 128) {
                utftext += String.fromCharCode(c);
            } else if ((c > 127) && (c < 2048)) {
                utftext += String.fromCharCode((c >> 6) | 192);
                utftext += String.fromCharCode((c & 63) | 128);
            } else {
                utftext += String.fromCharCode((c >> 12) | 224);
                utftext += String.fromCharCode(((c >> 6) & 63) | 128);
                utftext += String.fromCharCode((c & 63) | 128);
            }
        }
        return utftext;
    };
    var x = Array();
    var k, AA, BB, CC, DD, a, b, c, d;
    var S11 = 7,
        S12 = 12,
        S13 = 17,
        S14 = 22;
    var S21 = 5,
        S22 = 9,
        S23 = 14,
        S24 = 20;
    var S31 = 4,
        S32 = 11,
        S33 = 16,
        S34 = 23;
    var S41 = 6,
        S42 = 10,
        S43 = 15,
        S44 = 21;
    string = md5_Utf8Encode(string);
    x = md5_ConvertToWordArray(string);
    a = 0x67452301;
    b = 0xEFCDAB89;
    c = 0x98BADCFE;
    d = 0x10325476;
    for (k = 0; k < x.length; k += 16) {
        AA = a;
        BB = b;
        CC = c;
        DD = d;
        a = md5_FF(a, b, c, d, x[k + 0], S11, 0xD76AA478);
        d = md5_FF(d, a, b, c, x[k + 1], S12, 0xE8C7B756);
        c = md5_FF(c, d, a, b, x[k + 2], S13, 0x242070DB);
        b = md5_FF(b, c, d, a, x[k + 3], S14, 0xC1BDCEEE);
        a = md5_FF(a, b, c, d, x[k + 4], S11, 0xF57C0FAF);
        d = md5_FF(d, a, b, c, x[k + 5], S12, 0x4787C62A);
        c = md5_FF(c, d, a, b, x[k + 6], S13, 0xA8304613);
        b = md5_FF(b, c, d, a, x[k + 7], S14, 0xFD469501);
        a = md5_FF(a, b, c, d, x[k + 8], S11, 0x698098D8);
        d = md5_FF(d, a, b, c, x[k + 9], S12, 0x8B44F7AF);
        c = md5_FF(c, d, a, b, x[k + 10], S13, 0xFFFF5BB1);
        b = md5_FF(b, c, d, a, x[k + 11], S14, 0x895CD7BE);
        a = md5_FF(a, b, c, d, x[k + 12], S11, 0x6B901122);
        d = md5_FF(d, a, b, c, x[k + 13], S12, 0xFD987193);
        c = md5_FF(c, d, a, b, x[k + 14], S13, 0xA679438E);
        b = md5_FF(b, c, d, a, x[k + 15], S14, 0x49B40821);
        a = md5_GG(a, b, c, d, x[k + 1], S21, 0xF61E2562);
        d = md5_GG(d, a, b, c, x[k + 6], S22, 0xC040B340);
        c = md5_GG(c, d, a, b, x[k + 11], S23, 0x265E5A51);
        b = md5_GG(b, c, d, a, x[k + 0], S24, 0xE9B6C7AA);
        a = md5_GG(a, b, c, d, x[k + 5], S21, 0xD62F105D);
        d = md5_GG(d, a, b, c, x[k + 10], S22, 0x2441453);
        c = md5_GG(c, d, a, b, x[k + 15], S23, 0xD8A1E681);
        b = md5_GG(b, c, d, a, x[k + 4], S24, 0xE7D3FBC8);
        a = md5_GG(a, b, c, d, x[k + 9], S21, 0x21E1CDE6);
        d = md5_GG(d, a, b, c, x[k + 14], S22, 0xC33707D6);
        c = md5_GG(c, d, a, b, x[k + 3], S23, 0xF4D50D87);
        b = md5_GG(b, c, d, a, x[k + 8], S24, 0x455A14ED);
        a = md5_GG(a, b, c, d, x[k + 13], S21, 0xA9E3E905);
        d = md5_GG(d, a, b, c, x[k + 2], S22, 0xFCEFA3F8);
        c = md5_GG(c, d, a, b, x[k + 7], S23, 0x676F02D9);
        b = md5_GG(b, c, d, a, x[k + 12], S24, 0x8D2A4C8A);
        a = md5_HH(a, b, c, d, x[k + 5], S31, 0xFFFA3942);
        d = md5_HH(d, a, b, c, x[k + 8], S32, 0x8771F681);
        c = md5_HH(c, d, a, b, x[k + 11], S33, 0x6D9D6122);
        b = md5_HH(b, c, d, a, x[k + 14], S34, 0xFDE5380C);
        a = md5_HH(a, b, c, d, x[k + 1], S31, 0xA4BEEA44);
        d = md5_HH(d, a, b, c, x[k + 4], S32, 0x4BDECFA9);
        c = md5_HH(c, d, a, b, x[k + 7], S33, 0xF6BB4B60);
        b = md5_HH(b, c, d, a, x[k + 10], S34, 0xBEBFBC70);
        a = md5_HH(a, b, c, d, x[k + 13], S31, 0x289B7EC6);
        d = md5_HH(d, a, b, c, x[k + 0], S32, 0xEAA127FA);
        c = md5_HH(c, d, a, b, x[k + 3], S33, 0xD4EF3085);
        b = md5_HH(b, c, d, a, x[k + 6], S34, 0x4881D05);
        a = md5_HH(a, b, c, d, x[k + 9], S31, 0xD9D4D039);
        d = md5_HH(d, a, b, c, x[k + 12], S32, 0xE6DB99E5);
        c = md5_HH(c, d, a, b, x[k + 15], S33, 0x1FA27CF8);
        b = md5_HH(b, c, d, a, x[k + 2], S34, 0xC4AC5665);
        a = md5_II(a, b, c, d, x[k + 0], S41, 0xF4292244);
        d = md5_II(d, a, b, c, x[k + 7], S42, 0x432AFF97);
        c = md5_II(c, d, a, b, x[k + 14], S43, 0xAB9423A7);
        b = md5_II(b, c, d, a, x[k + 5], S44, 0xFC93A039);
        a = md5_II(a, b, c, d, x[k + 12], S41, 0x655B59C3);
        d = md5_II(d, a, b, c, x[k + 3], S42, 0x8F0CCC92);
        c = md5_II(c, d, a, b, x[k + 10], S43, 0xFFEFF47D);
        b = md5_II(b, c, d, a, x[k + 1], S44, 0x85845DD1);
        a = md5_II(a, b, c, d, x[k + 8], S41, 0x6FA87E4F);
        d = md5_II(d, a, b, c, x[k + 15], S42, 0xFE2CE6E0);
        c = md5_II(c, d, a, b, x[k + 6], S43, 0xA3014314);
        b = md5_II(b, c, d, a, x[k + 13], S44, 0x4E0811A1);
        a = md5_II(a, b, c, d, x[k + 4], S41, 0xF7537E82);
        d = md5_II(d, a, b, c, x[k + 11], S42, 0xBD3AF235);
        c = md5_II(c, d, a, b, x[k + 2], S43, 0x2AD7D2BB);
        b = md5_II(b, c, d, a, x[k + 9], S44, 0xEB86D391);
        a = md5_AddUnsigned(a, AA);
        b = md5_AddUnsigned(b, BB);
        c = md5_AddUnsigned(c, CC);
        d = md5_AddUnsigned(d, DD);
    }
    return (md5_WordToHex(a) + md5_WordToHex(b) + md5_WordToHex(c) + md5_WordToHex(d)).toLowerCase();
}

# es6导出使用
//module.exports = {
//    md5: md5
//}

小编今天在使用Sqlalchemy连接不同数据库,生成两个相同的表名的时候,出现了这样的错误Table 'XXX' is already defined for this MetaData instance. Specify 'extend_existing=True' to redefine options and columns on an existing Table object。导致第二个表无法生成,下面小编说下解决办法:

给第二个表使用一个新的metadata
代码示例:

from sqlalchemy import MetaData
 
class A(db.Model):
    __tablename__ = 'A'
 
    ···
 
class A(db.Model):
    __bind_key__ = 'newdb'
    __tablename__ = 'A'
    metadata = MetaData()
 
    ···