十分钟搞定你自己的多图片/文件服务器 - 坦荡 - 博客园

mikel阅读(918)

来源: 十分钟搞定你自己的多图片/文件服务器 – 坦荡 – 博客园

本文版权归博客园和作者吴双本人共同所有,转载和爬虫必须在显要位置注明原文地址,www.cnblogs.com/tdws

 写在前面

图片/文件服务器,顾名思义就是存文件呗,有的人用阿里云的现有服务,有的把文件Post到文件服务器,在文件服务器一端用一个应用程序来接收并保存,方法各不相同。老司机们各种服务器已经玩烂了。在使用图片/文件多服务器的几个问题就在于:

1.web上传的文件如何post到文件服务器上及其速度如何,占用带宽和耗时如何。

2.如何使文件均衡的分布在各台图片服务器上。

3.如何拓展新的图片服务器,并保障对已分配的内容影响降到最小。

本篇分享一方面希望有经验的司机给点意见,另一方面分享给新司机和记录自己的历程。

 第一分钟确定最终目标

1.各文件服务器不仅提供存储功能,还要有对外服务能力,以降低各个Web服务器的带宽占用和磁盘IO消耗。

2.提供简便和容易的可横向拓展能力

3.Async异步IO保证线程非阻塞,如果对异步有疑问的,可以异步这篇文章 http://www.cnblogs.com/tdws/p/6172207.html

4.文件快速到达图片服务器

 第三分钟否定自己

1.否认采取Http Post图片到图片服务器的方式

2.否认采取Hash取模算法

3.否认使用一致性哈希,避免服务器横向拓展后文件移动,即使是少量的

 第五分钟确定实现方式

1.把文件服务器磁盘共享出一个文件夹,作为特定用户间“网络磁盘”,就是说该共享文件夹,只有web服务器有权限访问,你可以参照局域网内如何共享文件夹或者阿里云共享文件夹。

2.通过IIS虚拟目录,将其物理路径指向网络磁盘。你知道共享文件夹间使用Tcp/Ip协议和Netbios协议通信。

3.配置WebConfig访问网络磁盘身份(用户名和密码)—必须配置哦

4.第一步所共享的文件,在文件服务器部署在IIS下WebApp中的一个文件下,以便暴露URL路径。因为你知道IIS除了使用文件读取的方式,是不可以获取父级文件夹中的任何内容的。

4.确定根据文件名均匀分配图片位置的方式—文件后缀名方式,后缀名采用”文件名-01″(一号服务器),后缀名采用”文件名-02″(二号服务器),随机数使用Random来取,随机数是几,我们就存在几号服务器。经过多次测试100万次随机,1,2,3分别得到33W次左右,保障了均衡。在将来拓展的时候,Random随机数多开启几位就行了,不需要文件的移动,也没有reshash。并且在横向拓展服务器数量后,希望保证已有三台服务器不继续存储文件,Random范围可调。在数据库或者缓存中我们已经存下了文件名称,并且其有后缀名,在为前台返回图片URL的时候,只需要根据后缀名称,来拼出文件服务器IP或域名。

 第七分钟按照步骤动手操作

1.部署FileServerApp,其功能是对图片暴露URL,其实很简单啦,建立一个ASP.NET空项目,建立出存储图片的文件夹。并将该文件夹共享给Web服务器,细节看最后一节的部署图。

2.假定你已经实现文件夹共享

3.在IIS站点下新建虚拟目录,注意物理路径指向的是网络共享文件夹的网络路径

4.在Webconfig下  <system.web>增加 <identity impersonate=”true” userName=”ben” password=”yourpwd” /> ,IIS对该虚拟目录设置身份验证为 应用程序池标识。

5.使用Server.MapPath可以获取FileServer2的网络路径,并使用你的读写文件方式对目录中的文件进行读写。

6.写入文件建议使用Async的方式,异步IO会在你将IO请求包发给网络驱动或者磁盘驱动后,返回去处理其他事情,在将来的某个时刻,文件写入成功,状态机恢复你的上下文并以同步的方式继续向下执行,能大大提高你Web服务器的吞吐能力。

 写在最后的重点

如果还有模糊的地方,看这里就全明白啦

 

 

2017-5-3更新。读淘宝技术这十年笔记,有兴趣可以看看。

 

一束激光冒充人声:110米外黑掉智能音箱,手机电脑平板也中招_IT新闻_博客园

mikel阅读(1246)

来源: 一束激光冒充人声:110米外黑掉智能音箱,手机电脑平板也中招_IT新闻_博客园

郭一璞 光栗子 发自 凹非寺
量子位 报道 公众号 QbitAI

周末的下午,你正在家里打游戏,不曾注意到身旁的智能音箱上,多出了一个小绿点。

那是一束激光,来自窗外马路对面的另一栋建筑。

突然,房间的灯开了。

空调、空气净化器、扫地机器人启动了,手机收到了电商平台的扣款提示,甚至你外面的车库门也已然洞开……

而你的手机和平板电脑也突然开始发疯,疯狂的下载删除不同的应用,播放奇怪的视频和音乐,给社交软件上的好友发奇怪的信息……

到底发生了什么?是谁,不知不觉侵入了你的生活?

其实,这是来自日本电气通信大学和美国密歇根大学科学家的一项新发现:

当激光打在装有语音助手的设备上,就可以冒充人类的语音,被麦克风转换成电信号,悄无声息的发出指令,控制相连的设备。

因此,那些和 Google Assistant、亚马逊 Alexa、苹果 Siri 相连的机器,不管是智能的灯具、门锁、电器这些硬件设施,还是各种电商、支付、社交 App,都会不知不觉间被控制。

虽然科学家们还没有在其他品牌的语音助手上测试,不过量子位采访到了腾讯安全团队 Tencent Blade Team,他们说:从原理上讲问题大都是相通的。

不需要太强的激光,普通激光笔的强度就可以,就算距离有110 米远,就算在外面的另一栋建筑里,就算要穿过玻璃窗,都可以控制你家里的智能音箱、手机和平板们。

恐怖的演示过程

来看看科学家们的实地演示。

将命令 Google Assistant 打开车库门的语句“OK Google, open the garage door”的嵌入激光中,打在智能音箱的麦克风上。

智能音箱回了一句“OK, opening”,接着车库门就开了。

那么如果把距离设置的非常远呢?

在第二段演示中,激光发射器和智能音箱的距离长达110 米

科学家们将询问时间的语句“OK Google, what time is it?”的嵌入激光中,打在智能音箱的麦克风上。

“It’s 9:43”在没有任何人发话询问的情况下,智能音箱自己突然说了一个时间。

即使在窗外的另一栋建筑里,也不影响激光对智能音箱的控制。

在第三段演示中,科学家们将激光源挪到了远处的一栋高高的建筑上,隔着玻璃窗发射激光,将命令 Google Assistant 打开车库门的语句“OK Google, open the garage door”的嵌入激光中,打在窗口智能音箱的麦克风上。

因为这次发射点又高又远,所以科学家们干脆给激光配了一个长焦镜头。

智能音箱还是顺利的回复“OK, opening”,打开了车库的门。

当然,演示中并没有“鬼故事现场”的感觉,一个原因是激光可见,另一个原因是语音助手的声音你可以听到。

因此,科学家们也尝试了人类肉眼看不见的红外光,在比较近的距离是可以起到作用的;

至于,语音助手回话会被主人听见,先用激光发个指令把音量调零,就真的悄无声息控制一切了。

看到这里你可能疑惑,激光怎么能冒充人声?

让麦克风听成人声

故事是从去年春天开始。

来自日本的菅原健,是个研究网络安全问题的科学家。他专程跑去美国,给密歇根大学的同行傅佳伟 (Kevin Fu) 教授,秀了一波自己刚刚解锁的技能:

把一束高强度的激光,对准iPad的麦克风,然后用每秒震荡大约 1000 次的正弦波,不停地调整激光的强度。

傅佳伟在一旁带着耳机,听麦克风收到了什么。让人惊讶的是,他听到了一种高频音调。

明明是接收声波的设备,却把光波当成声波接收了,这是MEMS 麦克风的一个重要弱点。而大部分手机和智能音箱,都是使用 MEMS 麦克风,因此。

自从有了这个神奇的发现,菅原君就开始和傅佳伟的实验室一起,用激光去欺骗智能音箱,攻击各种接收语音指令的设备。

科学家说,只要用一种特定的频率去调整激光的强度,激光便会用同样的频率去干扰麦克风,让麦克风把光波解调成电信号

就像下面这张图,上为激光发射的信号,下为麦克风接收的信号,频率几乎一致:

不用指定发射位置,只要对准麦克风射出激光,麦克风就把光线转换成电信号了,像日常把声波转换成电信号一样。

当然,如果只是随意的电信号,并不足以让音箱乖乖听你的话。必须让它以为是有人类发出语音才行。

所以,研究人员还要对激光做调幅 (AM) ,让麦克风转出接近人类语音的信号。

就像开头展示的那样,他们选定了一系列指令,包括:“现在几点了”“把音量调零”“买一支激光笔”“打开车库门”等等。

然后,用这些词句的语音波形,来定制激光的强度变化。

这样,智能音箱收到的电信号,就会和听到人声的时候差不多了。

一开始,他们用60 毫瓦的激光,测试了16 台不同的智能音箱。

结果,50 米是成功接收的最远距离。

攻击手机,就稍微困难一些了:iPhone 需要10 米以内,安卓手机需要5 米以内。

后来,科学家们又想测试一下,这项技术的极限在哪里。于是,把激光强度调低到了5 毫瓦,相当于一支廉价激光笔的水平,把距离拉远到110 米

虽然,许多音箱都没有响应,但Google Home和初代Echo Plus依旧中了招,就是开头看到的那样。

进一步加大难度,隔着窗户发射激光,76 米距离。这次没骗到一只 Echo,但 Google Home 依然被骗了,堪称硕果仅存:

至于,麦克风为什么对光波也有反应,哈佛大学电气工程系的退休教授 Paul Horowitz 说,至少有两种物理机制,可以让麦克风把误解成声波

一是激光的脉冲会加热麦克风的振膜,令周围的空气膨胀,产生一种压力。声音也是依靠产生声压,才被麦克风捕捉到的。

二是,如果被攻击的设备,不是完全不透光的话,光线其实可以直接穿过麦克风,直接到达芯片的所在,这样就能把光波的振动,翻译成电信号了。

这可能跟太阳能板里的二极管,还有光纤电缆末端的光电效应,原理一样。如果真是这样,想让激光被当做语音指令,就更容易了。

另外,量子位还采访到了腾讯的安全团队 Tencent Blade Team,他们的理解是:

这项研究和此前业界的 “海豚音攻击”有异曲同工之妙,都是利用了麦克风的一些特殊的硬件特性进行攻击,但这次的“光攻击”可以从更远距离(超过 100 米)发起,在现实生活中的攻击利用难度更低。

攻击的难度降低,防御的难度提升。Blade 团队认为,这项研究的意义十分重大:

从目前公开的信息来看,厂商很难从软件层面对这个漏洞进行彻底修复,之前安全圈内也没有 MEMS 麦克风会将光信号转换为电信号相关的安全研究,这项研究还是具有很高的创新性与实战意义。

怎样才能不被黑?

看到这样的攻击效果,谷歌和亚马逊很快就回应了:

谷歌说,已经在仔细观察这次的研究成果,并且强调一直对保护用户、提升设备的安全性能非常重视。

亚马逊也发表了声明说,正在看论文,后面将会和作者们交流,更深入地了解这项研究。

在厂商们给出补救措施之前,研究人员先为他们提供了一些友善的建议:

比如,可以设置让用户先输入语音密码,解锁后才能发布指令。

比如,可以在麦克风周围加上光屏蔽,抵挡激光的攻击。

再比如,在音箱两侧依靠两个麦克风同时接收指令,然后对比。因为两个麦克风,很难同时被击中。

当然,对产品做出这样的升级,还需要不少时间。

而你现在能做的就是,不要把智能音箱放在黑客能看到的地方。

不然,还是去用那些需要解锁的设备吧,人脸解锁和指纹解锁都能起到保护作用,避免语音助手接收到黑客的指令。

腾讯 Blade Team 还提示,最好关闭声纹识别 (因为声纹也可以用激光冒充) ,也可以在设备外部的麦克风口贴上黑色标签纸,阻挡激光攻击。

作者们


一作菅原健,就是从日本跑到美国炫 (mian) 技 (ji) 的那一位,电子通信大学 (UBE) 的准教授。


傅佳伟 (Kevin Fu) ,密歇根大学的教授,专注攻击各种 AI。量子位之前报道过一种把硬盘改造成窃听器的方法,也是他参与的研究。难怪,菅原君会不远万里去找他。


Daniel Genkin,密歇根大学助理教授。他和傅佳伟都是这项研究的负责人。

另外,还有两位作者,他们是傅佳伟教授实验室的成员,Benjamin Cyr 以及 Sara Rampazzi。

传送门

论文

Light Commands: Laser-Based Audio Injection Attacks on Voice-Controllable Systems

Takeshi Sugawara, Benjamin Cyr, Sara Rampazzi, Daniel Genkin, Kevin Fu

https://lightcommands.com/20191104-Light-Commands.pdf

主页

https://lightcommands.com/

日本中二少年教你用姿势估计把自己变成3D人物,动作实时同步,iOS上也能实现

mikel阅读(1383)

来源: 日本中二少年教你用姿势估计把自己变成3D人物,动作实时同步,iOS上也能实现

大数据文摘出品
作者:刘俊寰
不知道从什么时候开始,3D动画就热起来了,但是很多经典动画3D化后就变味了,人物的肢体动作看上去僵硬了不少。并且,传统3D靠一帧一帧制作,费时费力。
现在,你就拥有一个拯救3D动画的机会!
一位日本中二少年自学了机器学习后,就给自己做了个酷炫的模型,可以把自己的动作实时变成流畅的3D人物动作,而且整个过程非常简单易操作。
话不多说先看效果图:
这个推特名为幸彦青柳(Yukihiko Aoyagi)的日本小哥将3D姿态估计与3D开发平台和一些渲染引擎(比如Unity)相结合,于此更够跟准确地跟踪3D空间中的人体运动。上面的动图就是针对动作的实时估计和生成。
不过可惜的是,这个项目目前还只支持单人动作,不能实现双人对打。
项目已经在GitHub上开源:
https://github.com/yukihiko/ThreeDPoseUnitySample?source=post_page—–e74d7d347c2———————-
趁着它还没刷爆朋友圈,赶紧上手试一试!
用3D姿势估计的Onnx模型移动Unity

青柳君尝试过多种实现方式,包括WindowsML,ML.Net,Onnx Runtime等,但最终选择了OpenCVSharp,也就是OpenCV模型导入功能,在Unity中加载和执行Onnx,因为OpenCVSharp在Unity和.Net环境中可以用相同的方式处理,图像也不会被转换为Mat格式。
尽管看上去处理起来很容易,但目前还缺少相关数据,青柳君特意总结了他的这次尝试,将文章公布在了Qiita上。
相关链接:
https://qiita.com/yukihiko_a/items/386e3a86a5e523757707
有关Onnx的代码部分如下:
// Properties for onnx and estimation    private Net Onnx;    private Mat[] outputs = new Mat[4];
    private const int inputImageSize = 224;    private const int JointNum = 24;    private const int HeatMapCol = 14;    private const int HeatMapCol_Squared = 14 * 14;    private const int HeatMapCol_Cube = 14 * 14 * 14;
    char[] heatMap2Dbuf = new char[JointNum * HeatMapCol_Squared * 4];    float[] heatMap2D = new float[JointNum * HeatMapCol_Squared];    char[] offset2Dbuf = new char[JointNum * HeatMapCol_Squared * 2 * 4];    float[] offset2D = new float[JointNum * HeatMapCol_Squared * 2];
    char[] heatMap3Dbuf = new char[JointNum * HeatMapCol_Cube * 4];    float[] heatMap3D = new float[JointNum * HeatMapCol_Cube];    char[] offset3Dbuf = new char[JointNum * HeatMapCol_Cube * 3 * 4];    float[] offset3D = new float[JointNum * HeatMapCol_Cube * 3];
    public void InitONNX()    {        Onnx = Net.ReadNetFromONNX(Application.dataPath + @"\MobileNet3D2.onnx");        for (var i = 0; i < 4; i++) outputs[i] = new Mat();    }
    /// <summary>    /// Predict    /// </summary>    /// <param name="img"></param>    public void Predict(Mat img)    {        var blob = CvDnn.BlobFromImage(img, 1.0 / 255.0, new OpenCvSharp.Size(inputImageSize, inputImageSize), 0.0, false, false);        Onnx.SetInput(blob);        Onnx.Forward(outputs, new string[] { "369", "373", "361", "365" });
        // copy 2D outputs        Marshal.Copy(outputs[2].Data, heatMap2Dbuf, 0, heatMap2Dbuf.Length);        Buffer.BlockCopy(heatMap2Dbuf, 0, heatMap2D, 0, heatMap2Dbuf.Length);        Marshal.Copy(outputs[3].Data, offset2Dbuf, 0, offset2Dbuf.Length);        Buffer.BlockCopy(offset2Dbuf, 0, offset2D, 0, offset2Dbuf.Length);        for (var j = 0; j < JointNum; j++)        {            var maxXIndex = 0;            var maxYIndex = 0;            jointPoints[j].score2D = 0.0f;            for (var y = 0; y < HeatMapCol; y++)            {                for (var x = 0; x < HeatMapCol; x++)                {                    var l = new List<int>();                    var v = heatMap2D[(HeatMapCol_Squared) * j + HeatMapCol * y + x];
                    if (v > jointPoints[j].score2D)                    {                        jointPoints[j].score2D = v;                        maxXIndex = x;                        maxYIndex = y;                    }                }
            }
            jointPoints[j].Pos2D.x = (offset2D[HeatMapCol_Squared * j + HeatMapCol * maxYIndex + maxXIndex] + maxXIndex / (float)HeatMapCol) * (float)inputImageSize;            jointPoints[j].Pos2D.y = (offset2D[HeatMapCol_Squared * (j + JointNum) + HeatMapCol * maxYIndex + maxXIndex] + maxYIndex / (float)HeatMapCol) * (float)inputImageSize;        }
        // copy 3D outputs        Marshal.Copy(outputs[0].Data, heatMap3Dbuf, 0, heatMap3Dbuf.Length);        Buffer.BlockCopy(heatMap3Dbuf, 0, heatMap3D, 0, heatMap3Dbuf.Length);        Marshal.Copy(outputs[1].Data, offset3Dbuf, 0, offset3Dbuf.Length);        Buffer.BlockCopy(offset3Dbuf, 0, offset3D, 0, offset3Dbuf.Length);        for (var j = 0; j < JointNum; j++)        {            var maxXIndex = 0;            var maxYIndex = 0;            var maxZIndex = 0;            jointPoints[j].score3D = 0.0f;            for (var z = 0; z < HeatMapCol; z++)            {                for (var y = 0; y < HeatMapCol; y++)                {                    for (var x = 0; x < HeatMapCol; x++)                    {                        float v = heatMap3D[HeatMapCol_Cube * j + HeatMapCol_Squared * z + HeatMapCol * y + x];                        if (v > jointPoints[j].score3D)                        {                            jointPoints[j].score3D = v;                            maxXIndex = x;                            maxYIndex = y;                            maxZIndex = z;                        }                    }                }            }
            jointPoints[j].Now3D.x = (offset3D[HeatMapCol_Cube * j + HeatMapCol_Squared * maxZIndex + HeatMapCol * maxYIndex + maxXIndex] + (float)maxXIndex / (float)HeatMapCol) * (float)inputImageSize;            jointPoints[j].Now3D.y = (float)inputImageSize - (offset3D[HeatMapCol_Cube * (j + JointNum) + HeatMapCol_Squared * maxZIndex + HeatMapCol * maxYIndex + maxXIndex] + (float)maxYIndex / (float)HeatMapCol) * (float)inputImageSize;            jointPoints[j].Now3D.z = (offset3D[HeatMapCol_Cube * (j + JointNum * 2) + HeatMapCol_Squared * maxZIndex + HeatMapCol * maxYIndex + maxXIndex] + (float)(maxZIndex - 7) / (float)HeatMapCol) * (float)inputImageSize;        }    }
模型输入224×224的图像,输出的关节数为24个,热图(Heatmap)为14×14。
2D热图格式是24x14x14,3D的是24x14x14x14。将其作为与热图的坐标偏移值,输出的2D(x,y)变为2x24x14x14,3D(x,y,z)变为3x24x14x14x14。
 public void InitONNX()    {        Onnx = Net.ReadNetFromONNX(Application.dataPath + @"\MobileNet3D2.onnx");        for (var i = 0; i < 4; i++) outputs[i] = new Mat();    }

首先,使用InitONNX()读取Onnx文件。
由于OpenCV的输出是通过Mat对象返回的,需要准备四个数组。
 public void Predict(Mat img)    {        var blob = CvDnn.BlobFromImage(img, 1.0 / 255.0, new OpenCvSharp.Size(inputImageSize, inputImageSize), 0.0, false, false);        Onnx.SetInput(blob);        Onnx.Forward(outputs, new string[] { "369", "373", "361", "365" });
        // copy 2D outputs        Marshal.Copy(outputs[2].Data, heatMap2Dbuf, 0, heatMap2Dbuf.Length);        Buffer.BlockCopy(heatMap2Dbuf, 0, heatMap2D, 0, heatMap2Dbuf.Length);        Marshal.Copy(outputs[3].Data, offset2Dbuf, 0, offset2Dbuf.Length);        Buffer.BlockCopy(offset2Dbuf, 0, offset2D, 0, offset2Dbuf.Length);

Predict方法参数的Mat对象是正常的CV_8UC3 Mat图像数据,需要将其转换为Blob Mat才能传递给Onnx,这个过程利用BlobFromImage就能完成。
在Output中,“369”和“373”是3D,“361”和“365”是2D。但如果是Mat对象,处理起来就稍微复杂一些,因为还需要将其转换为float数组。
然后,通过改变关节数和热图大小,找到最大热图。
由于3D是一个相当大的循环,最好再做一些改进,但是由于它现在移动得足够快,保持原样也是可以的。
在iOS上也能实现3D姿势估计

去年的日本黄金周,青柳君第一次接触机器学习,也一直在3D姿势估计这块有所钻研。
今年3月份,他在iOS上实现了3D姿势估计。据本人推特发言称,他用了一天时间学习,然后做出了这个模型。
根据青柳君本人介绍,iOS项目的学习环境是Windows10/PyTorch0.4,执行环境是iPhone XS Max,至于选择iPhone XS Max的原因,青柳君说,iPhone XS Max的A12处理器功能非常强大。
还是先看看效果如何:
  
青柳君准备了2D和3D的数据集,2D数据集是利兹运动姿势数据集,利兹运动姿势扩展训练数据集、MPII人类姿势数据集、Microsoft COCO;而3D数据集是原始数据集。
在此之前他还做了很多准备,包括从AssetStore购买的数据等,当然还有Unity。
然后就可以利用Unity创建3D角色动画了,创建角色图像和坐标,包括肩膀、肘部、手腕、拇指、中指、脚、膝盖、脚踝、脚趾、耳朵、眼睛、鼻子,以输出身体的中心位置,即肚脐。
该数据集由于许可原因结果变得十分复杂,导致发布失败。
由于这是CG,因此可以随意更改角色的纹理和姿势。最初,他希望更改每个时期数据集的内容,以提高泛化性能,但没有效果,为此大约有100,000个副本用于学习。
即使是用3D版本的图像,也可以照原样学习,最后可以获得相似的图像,但是无法获得预期的性能。
将通过PyTorch学习得到的模型导出到Onnx,用coremltools转换为CoreML模型,此时就算是估计到了相同的图像,结果也会有所不同,所以准确度未知。
将模型导入Mac,使用XCode的iPhone版本,通过实时捕获后方摄像机图像执行3D估计。
XS Max能以大约40fps的速度运行,但是,一段时间,手机会变热,速记也会下降至约30fps。如果仅用于学习2D模型,其运行速度会接近100fps。
由于这是个3D项目,显示时无法从摄像机看到的部分,判断热图的阈值已降低到几乎为零。例如,如果手臂正常可见,热图的最大部分为0.5或更高(最大值为1.0);如果看不到手臂,将得到0.2或0.1的值,阈值降低。
但就结果而言,无论身在何处,系统都可以判断为有人。
Adobe发布最新动作追踪软件

上周,Adobe也发布了一款用于视觉效果和动态图形软件After Effects,该软件的AI功能能够自动跟踪人体运动并将其应用于动画。
简单地说,就是能够把现实人物的动作直接转换成为动画。
与青柳君的机器学习项目的效果相差无几!
Adobe研究科学家Jimei Yang在演示中说,这一功能利用了Adobe的人工智能平台Sensei,该平台用超过10000张图像进行了训练,从而能够识别人体的关键点。
据了解,人体跟踪器在源视频中能够检测到人体的运动,胳膊、躯干和腿部的18个关节点将生成相关跟踪点,然后将跟踪点转移到动画角色上,利用该功能,快速创建2D人物动画根本不在话下!
怎么样,有没有觉得打开了新世界的大门?
当然,对于姿势估计的实现还远远不止现在的程度,未来希望不仅是青柳君和Adobe,有更多人都参与到这个领域的研究和学习中来,促进相关领域的发展。

干货来袭-整套完整安全的API接口解决方案 - hubro - 博客园

mikel阅读(731)

来源: 干货来袭-整套完整安全的API接口解决方案 – hubro – 博客园

在各种手机APP泛滥的现在,背后都有同样泛滥的API接口在支撑,其中鱼龙混杂,直接裸奔的WEB API大量存在,安全性令人堪优

在以前WEB API概念没有很普及的时候,都采用自已定义的接口和结构,对于公开访问的接口,专业点的都会做下安全验证,数据签名之类

反而现在,谁都可以用WEB API估接口,安全性早忘一边了,特别是外包小公司的APP项目,80%都有安全漏洞(面试了大半年APP开发得出的结论)

特在过年之前,整理了下在用的解决方案,本方案解决了

  • 数据安全问题
  • 标准消息结构
  • 接口测试程序
  • 接口文档体现

正文

数据结构

对于一个接口,返回的内容除了要返回业务数据外,还得返回处理状态,并且这个状态是在每个接口都得有

所以数据格式都会定义为:

数据头(描述数据信息)

———————————–

数据体(具体数据)

本文定义结构为

复制代码
/// <summary>
    /// 处理结果
    /// </summary>
    public class DealResult
    {
        /// <summary>
        /// 处理结果
        /// </summary>
        public bool Result
        {
            get;
            set;
        }
        /// <summary>
        /// 消息
        /// </summary>
        public string Message
        {
            get;
            set;
        }
        /// <summary>
        /// 关联数据
        /// </summary>
        public object Data
        {
            get;
            set;
        }
    }
复制代码

所有接口都返回此对象,会描述本次请求的状态,和对应的数据,服务端则根据实际情况,返回处理结果和对应的数据

 

数据安全

开方式接口安全性就不用多说了,解决方法为加密,或数据签名验证,本文方案为进行数据签名

同返回的数据一样,提交到服务器的数据格式也统一约定,定义一个数据头基类

复制代码
    /// <summary>
    /// 参数基类
    /// </summary>
    [Serializable]
    public class ParameBase
    {
        string time = DateTime.Now.ToString("yyyy-MM-dd hh:mm:ss");
        /// <summary>
        /// 时间 格式 yyyy-MM-dd hh:mm:ss
        /// </summary>
        public string Time
        {
            get
            {
                return time;
            }
            set
            {
                time = value;
            }
        }
        /// <summary>
        /// 来源网站 = 1, IOS = 2,Android = 3, 微信 = 4
        /// </summary>
        public int SourceFrom
        {
            get;
            set;
        }
        /// <summary>
        /// 签名
        /// </summary>
        public string Token
        {
            get;
            set;
        }
       
    }
复制代码

 

一个登录对象表示为

复制代码
    /// <summary>
    /// 登录
    /// </summary>
    public class Login : ParameBase
    {
        /// <summary>
        /// 用户名
        /// </summary>
        public string Name
        {
            get;
            set;
        }
        /// <summary>
        /// 密码
        /// </summary>
        public string Password
        {
            get;
            set;
        }
    }
复制代码

数据签名表示为(KEY稍后讲到)

Token=MD5(属性值1+值2….+KEY)

按此对象表示为 MD5(Name+PassWord+Source+Time+KEY)

如果是GET参数怎么办,一样,按参数名计算,同时传递的参数要附带上Source,Time,Token

 

密钥机制

有的喜欢把密钥放在客户端,或固定密钥,显然都有安全问题,解决方法是动态获取

这就意味着在设计接口时,有一个接口是首先要调用的,让服务器返回密钥,于是就有了登录的概念

过程表示为

登录>返回用户信息和密钥=>存储用户信息和密钥=>使用密钥调用其它接口

这样只有登录者和服务器才知道自已的密钥了

综上所述,数据结构表示为

客户端提交结构为 ParameBase(附带签名信息)

服务端返回结构为 DealResult

 

登录机制

同网页请求一样,怎么知道多次调用是同一个人呢,这里采用了COOKIE的形式,登录后服务端返回一个COOKIE,客户端再请求时带上这个COOKIE

服务端需要存储这个COOKIE标识,所有的验证处理都会基于此标识来判断用户

 

有了上面基础,进入项目阶段

WEB API项目

其实用什么项目类型都行,只是WEB API方便了对象结构序列化和传参

默认WEB API路由RESUFUL形式,没有控制器方法,只能按METHOD来定义,很不方便,改成控制器的形式,这样就能用方法名来访问了

更改路由配置为

1
2
3
4
5
config.Routes.MapHttpRoute(
                name: "DefaultApi",
                routeTemplate: "api/{controller}/{action}/{id}",//加上路由ACTION参数
                defaults: new { id = RouteParameter.Optional }
            );

在此文,数据分为请求和返回,以登录返回用户信息为例,登录为请求,用户信息为返回,示例对象结构为

用户对象

复制代码
/// <summary>
    /// 登录返回用户
    /// </summary>
    public class User
    {
        /// <summary>
        /// 用户编号
        /// </summary>
        public int Id
        {
            get;
            set;
        }
        /// <summary>
        /// 名称
        /// </summary>
        public string Name
        {
            get;
            set;
        }
        /// <summary>
        /// 本次登录的KEY
        /// </summary>
        public string Key
        {
            get;
            set;
        }
        /// <summary>
        /// 本资登录的凭证
        /// </summary>
        public string Voucher
        {
            get;
            set;
        }

       
    }
复制代码

 

请求方式

这里只采用了GET,POST两种方式,根据实际情况定义,控制器方法一定需要都标明,不然会出现路由BUG

定义登录方法

 View Code

这里可以看到,创建了两个GUID,一个为用户凭证,一个为用户密钥,放入用户信息返回,同时调用LoginStatusContext.SetLoginStatus保存登录信息

同时使用了AnonymousSign标注,此方法使用默认签名Setting.DefaultKey

定义获取用信息方法

复制代码
        /// <summary>
        /// 基本信息
        /// </summary>
        /// <param name="name">参数name</param>
        /// <returns>User</returns>
        [HttpGet]
        public DealResult GetBasicInfo(string name)
        {
            var user = new User() { Name = name, Id = CurrentUserId };
            return DealResult(true, string.Empty, user);
        }
复制代码

 

示例控制器完整定义

复制代码
 /// <summary>
    /// 帐号操作
    /// </summary>
    [SignCheckAttribute]
    public class AccountController : BaseController
    {
        /// <summary>
        /// 登录
        /// </summary>
        /// <param name="parame"></param>
        /// <returns>User</returns>
        [HttpPost]
        [AnonymousSign]
        public DealResult Login([FromBody] Login parame)
        {
            if (parame.Password != "123")
            {
                return DealResult(false, "密码不正确");
            }
            string key2 = System.Guid.NewGuid().ToString();
            string voucher = System.Guid.NewGuid().ToString();
            var user = new User() { Name = parame.Name, Id = 1, Key = key2, Voucher = voucher };
            var timeDiff = (DateTime.Now - Convert.ToDateTime(parame.Time)).TotalSeconds;//保存客户端和服务端时间差
            LoginStatusContext.SetLoginStatus(voucher, user.Id, key2, timeDiff);
            CoreHelper.CookieHelper.AddCookies("user", voucher);//存入COOKIE
            return DealResult(true, "", user);
        }


        /// <summary>
        /// 基本信息
        /// </summary>
        /// <param name="name">参数name</param>
        /// <returns>User</returns>
        [HttpGet]
        public DealResult GetBasicInfo(string name)
        {
            var user = new User() { Name = name, Id = CurrentUserId };
            return DealResult(true, string.Empty, user);
        }

        /// <summary>
        /// 测试异常
        /// </summary>
        /// <returns></returns>
        [HttpGet]
        public DealResult TestException()
        {
            int a = 0;
            var b = 10 / a;
            return DealResult(true);
        }

    }
复制代码

 

此控制器标注了SignCheckAttribute用以进行签名判断

具体实现可看SignCheckAttribute代码

SignCheckAttribute里实现了有

  • 数据签名判断
  • 签名超时判断
  • 用户登录限制
  • 签名重复使用处理(一个签名只能使用一次)
  • 过期登录用户处理(没有主动退出用户清理)

为了统一处理异常,配置了异常处理

1
GlobalConfiguration.Configuration.Filters.Add(new ExceptionAttribute());

对接口进行测试

大杀器来了,配合此方案放出了对应的测试工具,虽然WEB API有个扩展,但没法对此方案测试

使用此工具能方便按方案要求调用接口,为了方便参数拼接,POST和GET都采用URL参数的形式输入

测试登录/api/account/login

测试获取信息/api/account/GetBasicInfo

测试异常处理/api/account/TestException

在未登录情况下调用获取信息

接口文档

接口结构文档一直是很让人头疼的事,手写更改了又得维护,版本不一样还麻烦,自动生成最好了,同样WEB API 带扩展没法表示此结构详细

大杀器2号来了,按代码注释动态生成接口文档,文档格式与控制器保持一致

Home控制器代码实现

复制代码
    public ActionResult Index(SummaryAnalysis.ExportType exportType = SummaryAnalysis.ExportType.NONE)
        {
            if (exportType != SummaryAnalysis.ExportType.NONE)
            {
                var str = SummaryAnalysis.Load(exportType);
                return File(str, "application/octet-stream", "Model_" + exportType + ".zip");
            }
            else
            {
                if (string.IsNullOrEmpty(outPut))
                {
                    outPut = SummaryAnalysis.Load(exportType);
                }
                ViewBag.OutPut = outPut;
                return View();
            }
        }
    }
复制代码

在见过的开发文档,我觉得这是最好的展现形式了,还有锚点,快速定位到对象结构,并且与源代码保持一致

附WEB API 自带文档生成区别

附上项目源码

http://pan.baidu.com/s/1c2rDacK

项目结构:

———-WPF测试程序

———-接口示例

虽然跟CRL快速开发框架无关,但还是加上CRL的名,好文要顶!

如何设置宝塔面板优化 php 服务器性能-魏艾斯博客

mikel阅读(1360)

来源: 如何设置宝塔面板优化 php 服务器性能-魏艾斯博客

为了提高 php 服务器性能,安装宝塔面板的服务器需要设置一下。整个设置过程很简单,魏艾斯博客把如何设置宝塔面板提高服务器性能的过程写下来,希望能帮到初次使用宝塔面板的朋友们。

群里面太多朋友初次接触 php 环境和宝塔面板,而且很多人用 avada、The 7、newspaper、Enfold、Betheme、 Richer 等耗费系统资源的 wordpress 模板,如果不做额外优化真的打开很慢,比如一个朋友使用阿里云 ECS 国内服务器,2 核 8g 内存 40g 系统盘 100g 数据盘 5m 带宽,newspaper 主题,艺术类网站以图片为主,首页就是一个大 banner 和 N 多图片,全高清高分辨率那种的,联通百兆光纤打开需要近 10 秒钟,他自己也很不满意。要知道国内带宽成本极高,一台云服务器接近一半成本是带宽费用,普通网站 1M 带宽起步也不错了而他买了 5M 还慢,所以不优化一下真的不好意思让客户上来看。

宝塔面板是我们再熟悉不过的国产 Linux 面板了,目前在国内可以说是风声水起,用户量不断上涨。有关宝塔面板的安装使用在魏艾斯博客里面已经写过很多了,不再啰嗦。为了让服务器运行速度更快,我们就使用宝塔面板提供的功能来优化一下服务器配置。本文部分内容参考自@很文博客,老魏根据大家遇到的问题和自己的经验加以补充和丰富。

关于宝塔面板的入门基础知识:

1、定期释放内存

添加计划任务,可以设置每天或一周释放一次,间隔时间根据自己网站情况定,执行时机为半夜,如:1:30。

看下图,在宝塔面板左侧的计划任务中操作即可。

2、添加 Swap

关于 Swap 的重要性在lnmp 中创建、启用 SWAP 分区详细教程中已经写过了,无论服务器内存多大都必须添加 Swap。在宝塔面板的软件管理>>系统工具中找到 Linux 工具箱 1.4,点击最右侧的“设置”打开 Swap 虚拟内存,按照下面的推荐值进行设置。

Swap 推荐值:2G 和 2G 以下内存的服务器,设置成和物理内存相同容量 SWAP;2G 以上的,设置为 2G。如果跑的程序特别耗费内存,2G 内存以上的 Swap 也可以设置与内存相同。

3、安装 PHP 缓存扩展

个人博客的 php 缓存扩展推荐使用 Opcache 脚本缓存和 Memcached 内容缓存。在软件管理>>你使用的 php 版本最右侧点击“设置”,进入安装扩展,右侧点击这两个扩展后面的“安装”,再确认一次,很快搞定。

内容缓存和脚本缓存都只安装一个,不要同时安装多个。

其他的扩展组件,在确认用不到的前提下,都卸载掉,因为开着还不用会浪费系统资源。

4、php 配置调整

同样在 php 管理的配置修改中,memory_limit 脚本内存限制修改成 256M,这样 wordpress 跑起来更顺畅了;upload_max_filesize 允许上传文件的最大尺寸,像 avada 模板可能会超过这个数值无法上传,需要修改为大于上传文件的数值。

在配置文件中 Ctrl+F 搜索 memory_limit,把默认值修改成 256M,保存。下图是老魏改好后的。

5、PHP 并发调整

宝塔面板提供了几个优化过的 php 并发方案,一般 1 核 1G 内存的云服务器,设置 30 并发或 50 并发都可以,其他配置请自己测试一下合适并发方案。不建议并发超过 300 ,如果并发不够用还是优化数据库缓存可能更有效。

6、php 版本和 MySQL 数据库版本

php7 的性能比低版本有很大提升,老魏强烈建议使用 php7 以上版本;1 核 1G 配置的数据库推荐 MySQL5.5,而 5.6 要求最低内存 1G 以上,所以大家应该知道如何选择了。

7、卸载多余扩展组件

如果程序不需要,就卸载 ZendGuardLoader、ioncube、PATH_INFO,安装后不用也一直占用一部分内存。

设置完了要重启服务器,让调整后的参数生效。

8、像本文开头提到的群友网站高清图片较多的,如果达到几千张且还要不停增加的话,建议先点我领取千元代金券,再购买阿里云 ECS 云服务器的基础上搭配阿里云存储 OSS,把图片分流到 OSS 存储空间上去,ECS 只放程序部分,这样网站打开速度会快很多的。

9、WordPress 如何优化提升速度,这是个永恒的任务。想让网站加载速度快,优化的关键是找到平衡点,点我打开在线课程开始学习,全面分析原因并给出相对应的一整套解决方案。

以上优化方案分别在阿里云服务器(点我领取千元代金券 抵用 50 元起)和腾讯云服务器(点我五折优惠购买)上测试通过。

宝塔面板官方网站:点我直达

1、宝塔官方赠送3188元优惠券礼包。

2、新用户可享受0.99元体验1个月专业版。

经过上面的一番折腾整理优化,你的云服务器应该比之前流畅很多了,没有做到的同学赶紧去试试吧,也可以在下面留言交流你认为更合适的优化方案。

宝塔面板安装memcached的误区及正确方法-魏艾斯博客

mikel阅读(1508)

来源: 宝塔面板安装memcached的误区及正确方法-魏艾斯博客

宝塔面板添加memcached可以有效加速服务器访问速度,提高网站的用户体验。不过在实际使用中,魏艾斯博客发现很多新手有一个误区,就是添加了错误的memcached组件,导致起不到正常加速服务器的效果。本文中魏艾斯博客分享一下误区在哪里以及应该怎么安装是正确的加速组件。

参考资料:宝塔面板安装memcached及详细配置方法

宝塔面板官方网址:点我进入

1、宝塔官方赠送3188元优惠券礼包。

2、新用户可享受0.99元体验1个月专业版

1、错误的memcached组件添加方法

很多人直接安装下图中蓝色M字样的memcached组件。从这里安装后,服务器不会有加速效果,访问网站也不会感觉到比以前快。

2、正确的memcached安装方法

以老魏目前使用的php版本为例,正确安装方法是在“软件管理”的php7.0>设置中,找到“安装扩展”右侧的memcached,安装。这才是正确的memcached组件。安装完成后,再返回看到上图蓝色M字样的memcached已经自动安装上去了,无需人工干预。

也就是说这里有一个顺序问题,老魏测试了一下,如果你先安装上图蓝色M字样的Memcached,那么下图php7.0的memcached不会被安装,同样也起不到加速效果,失败;若先去php7.0中安装memcached,再返回会看到蓝色M字样的Memcached已经被自动安装好了,这时候服务器会加速,网站打开也会成倍提速,这才是正确的安装步骤和效果。请大家一定注意这个顺序不能乱搞,搞错了你会埋怨老魏分享错误、无效的资料,浪费你的时间、精力,却还没效果,其实是你把这个顺序搞颠倒了。

安装memcached组件后,还要安装一个插件,叫 memcached is your friend 这个插件。具体安装过程请移步安装 memcached 和 object-cache.php 中的第二步提到的这个插件,按照要求安装就可以了。宝塔面板如果不安装的话,memcached就不会正常工作了,命中率一直是0。

3、用宝塔面板加速

通过宝塔面板优化服务器速度、性能的参考资料如何设置宝塔面板优化 php 服务器性能,写的很详细了,这里不再啰嗦。

这个memcached是从服务器层面加速,比wordpress插件那种程序层面加速要快上很多,占用资源也小。因为wordpess缓存插件的原理是把数据库内容缓存到硬盘中,而memcached是缓存到内存中。内存的读取速度是几倍于硬盘读取速度的。不过你使用了memcached就必须卸载掉wordpress super cache之类的缓存插件,因为都是缓存插件会互相影响效果。

至于memcached的效果如何检测,有两种方法:一是使用宝塔面板自带的检测功能,不够直观;二是使用第三方php文件,很直观。操作起来很简单,请移步多种方法实时监测 Memcached命中率

ECSHOP怎樣可以取消用手机上网时,不自动转到mobile页面 - wpindesign - 博客园

mikel阅读(760)

来源: ECSHOP怎樣可以取消用手机上网时,不自动转到mobile页面 – wpindesign – 博客园

打开 index.php 删除以下代码

$ua = strtolower($_SERVER['HTTP_USER_AGENT']);

$uachar = "/(nokia|sony|ericsson|mot|samsung|sgh|lg|philips|panasonic|alcatel|lenovo|cldc|midp|mobile)/i";

if(($ua == '' || preg_match($uachar, $ua))&& !strpos(strtolower($_SERVER['REQUEST_URI']),'wap'))
{
$Loaction = 'mobile/';

if (!empty($Loaction))
{
ecs_header("Location: $Loaction\n");

exit;
}

宝塔面板的ftp无法使用解决 - 代码仔-兴江 - 博客园

mikel阅读(1027)

来源: 宝塔面板的ftp无法使用解决 – 代码仔-兴江 – 博客园

宝塔面板的ftp无法使用解决

先检查这些内容

1.注意内网IP和外网IP

2.检查ftp服务是否启动 (面板首页即可看到)

3.检查防火墙20端口 ftp 21端口及被动端口39000 – 40000是否放行 (如是腾讯云/阿里云等还需检查安全组)

注意:最好到配置文件修改端口一致

PassivePortRange 39000 40000

截图:

 

 

放行对应的防火墙端口:

 

 

4.是否主动/被动模式都不能连接

5.新建一个用户看是否能连接

6.更换ftp客户端使用flashfxp 如图勾上 再尝试连接

 

 

但凡解决了就可以登陆了,如果还是不行,那么试试最后一种方法:

登陆宝塔面板管理系统,找到左侧的“软件管理”–“FTP软件”—点击“设置”

 

 

然后点击配置修改:如图所示,查找“ForcePassiveIP”(位置188行左右)

# ForcePassiveIP                192.168.0.1

删除前面的“#”将“192.168.0.1”修改为:服务器的IP地址,是服务器不是你客户端的IP地址!

ForcePassiveIP  服务器公网ip地址

 

 

最后修改完记得重新启动一下FTP软件,打开FTP连接,你会发现奇迹发生了

win10 中ie浏览器闪一下就没有了应该怎么处理 - 系统族

mikel阅读(2533)

WIndows10系统中主流浏览器已经变成了Edge,但是有些网站指定需要使用ie打开。有些小伙伴打开IE一会就闪退那要怎么解决呢?别急,下面就给

来源: win10 中ie浏览器闪一下就没有了应该怎么处理 – 系统族

WIndows 10系统中主流浏览器已经变成了Edge,但是有些网站指定需要使用ie打开。有些小伙伴打开IE一会就闪退那要怎么解决呢?别急,下面就给大家简单细说一下。

系统推荐:win10系统下载

1、原因分析:出现这样的情况一般是由于ie中【第三方浏览扩展】导致的

解决方法一:按住windows+r调出运行,在框中输入:inetcpl.cpl  点击确定;

win10 中ie浏览器闪一下就没有了应该怎么处理

2、在【internet  属性】界面点击【高级】选卡,在下面框中将“启用第三方浏览器扩展”和“启用自动崩溃恢复”的勾去掉,点击应用并确定即可。

win10 中ie浏览器闪一下就没有了应该怎么处理

上面就是关于win10打开ie就闪退的解决方法,有遇到这个问题的小伙伴可以按照小编的步骤实地操作一下,想了解更多win10系统的知识,请大家关注系统族!