20221216-蓝牙模块与单片机如何连接

https://zhuanlan.zhihu.com/p/63449607

USB转TTL

TTL 是一种电平

像我们常用的单片机,引出来的串口,如果不加其他的接口电路,出来的信号就是TTL 电平,如果需要查看串口的打印信息,一般是需要接一个上位机的,常规的就是电脑,而现在的电脑一般的通信接口只有USB 老式的电脑可能会有 232 接口

对于usb口而言,是没法和TTL 串口直接通信的,因为接口不匹配,电平也不匹配,这时候就需要借助外围的小设备来实现接口与电平的转换,常规操作是使用CH340 这种芯片所制作的usb 转串口模块,这个芯片可以实现TTL 串口和USB 之前的转换

20221216-C语言中typedef的使用方法

http://c.biancheng.net/view/298.html

C语言用户使用typedef 关键字来定义自己习惯的数据类型名称,来替代系统默认的基本类型,数组类型名称,指针类型名称与用户自定义的结构型名称,共用型名称,枚举型名称等,一旦用户在程序中定义了自己的数据类型名称,就可以在该程序中用自己的数据类型名称来定义变量的类型,数组的类型,指针变量的类型与函数的类型等

C99 之前并未提供布尔类型,但我们可以使用typedef 关键字来定义一个简单的布尔类型,如下面的代码所示

1
2
3
4
5
typedef int BOOL;
#define TRUE 1
#define FALSE 0

BOOL bflag=TRUE;

C99 标准中新增了布尔类型,自定义布尔变量的方式有两种,一种是使用 _BOOL,另一种是使用<stdbool.h>头文件中定义好的 bool 类型,感兴趣的小伙伴请猛击这里了解详情。

typedef 的四种用法

在实际使用中,typedef 的应用主要有如下4种

为基本数据类型定义新的类型名

也就是说,系统默认的所有基本类型都可以利用typedef 关键字来重新定义类型名,示例代码如下所示

1
typedef unsigned int COUNT;

而且我们还可以使用这种方法来定义与平台无关的类型,比如,要定义一个REAL 的浮点类型,在目标平台上,让它表示最高精度的类型

1
typedef long double REAL;

在不支持 long double 的平台二上,改为:

1
typedef double REAL;

甚至还可以在连 double 都不支持的平台三上,改为:

1
typedef float REAL;

这样,当跨平台移植程序时,我们只需要修改一个typedef 的定义即可,而不用对其他源代码做任何修改,其实,标准库中广泛地使用了这个技巧,比如 size_t 在 VC++2010 的 crtdefs.h 文件中的定义如下所示:

1
2
3
4
5
6
7
8
#ifndef _SIZE_T_DEFINED
#ifdef _WIN64
typedef unsigned __int64 size_t;
#else
typedef _W64 unsigned int size_t;
#endif
#define _SIZE_T_DEFINED
#endif

为自定义数据类型 定义简洁的类名名称

以结构体为例,下面我们定义一个名为point的结构体

1
2
3
4
5
6
struct Point
{
double x;
double y;
double z;
};

在调用这个结构体时,我们必须像下面的代码这样来调用这个结构体

1
2
struct Point oPoint1={1001000};
struct Point oPoint2;

这里,结构体 struct Point 为新的数据类型,在定义变量的时候均要向上面的调用方法一样有保留字struct ,而不能像int 和double 那样直接使用Point 来定义变量,现在,我们利用typedef 定义这个结构体,如下面的代码所示

1
2
3
4
5
6
typedef struct tagPoint
{
double x;
double y;
double z;
} Point;

在上面的代码中,实际上完成了两个操作
定义了一个新的结构类型,代码如下所示

1
2
3
4
5
6
struct tagPoint
{
double x;
double y;
double z;
} ;

其中,struct 关键字和tagPoint 一起构成了这个结构类型,无论是否存在typedef 关键字,这个结构都存在

使用typedef 为这个新的结构起了一个别名,叫Point 即

typedef struct tagPoint Point

因此,现在你就可以像int 和double 那样直接使用Point 定义变量,如下面的代码所示

Point oPoint1={100,100,0};
Point oPoint2;

为了加深对typedef 的理解,我们再来看一个结构体例子,如下面的代码所示

1
2
3
4
5
typedef struct tagNode
{
char *pItem;
pNode pNext;
} *pNode;

从表面上看,上面的示例代码与前面的定义方法相同,所以应该没有什么问题,但是编译器却报了一个错误,

其实问题并非在于struct 定义的本身,大家应该都知道,C 语言是允许在结构中包含指向它自己的指针的,我们可以在建立链表等数据结构的实现上看到很多这类例子,那问题在哪里呢 ?其实,根本问题还是在于typedef 的应用

在上面的代码中,新结构建立的过程中遇到了pNext 声明,其类型是pNode 这里要特别注意的是,pNode 表示的是该结构体的新别名。于是问题出现了,在结构体类型本身还没有建立完成的时候,编译器根本就不认识pNode 因为这个结构体类型的新别名还不存在,所以自然就会报错,因此,我们要做一些适当的调整,比如将结构体中的pNext 声明修改成如下方式

1
2
3
4
5
typedef struct tagNode
{
char *pItem;
struct tagNode *pNext;
} *pNode;

或者将struct 与typedef 分开定义,如下面的代码所示

1
2
3
4
5
6
typedef struct tagNode *pNode;
struct tagNode
{
char *pItem;
pNode pNext;
};

在上面的代码中,我们同样使用typedef 给一个还未完全声明的类型tagNode 起了一个新别名,

不过,虽然C 语言编译器完全支持这种做法,但不推荐这样做,建议还是使用如下规范定义方法

1
2
3
4
5
6
struct tagNode
{
char *pItem;
struct tagNode *pNext;
};
typedef struct tagNode *pNode;

为数组定义简洁的类型名称

它的定义方法很简单,与为基本数据类型定义新的别名方法一样,示例代码如下所示,

1
2
typedef int INT_ARRAY_100[100];
INT_ARRAY_100 arr;

为指针定义简洁的名称

对于 指针,我们同样可以使用下面的方式来定义一个新的别名

1
2
typedef char* PCHAR;
PCHAR pa;

对于下面这种简单的变量声明,使用typedef 来定义一个新的别名或许会感觉意义不大,但在比较复杂的变量声明中,typedef 的优势马上就体现出来了,如下面的示例代码所示

1
int *(*a[5])(int,char*);

对于上面变量的声明,如果我们使用typedef 来给它定义一个别名,这会非常有意义,如下面的代码所示

1
2
3
4
// PFun是我们创建的一个类型别名
typedef int *(*PFun)(int,char*);
// 使用定义的新类型来声明对象,等价于int*(*a[5])(int,char*);
PFun a[5];

小心使用typedef 带来的陷阱

接下来看一个简单的typedef 使用示例,如下面的代码所示

1
2
typedef char* PCHAR;
int strcmp(const PCHAR,const PCHAR);

在上面的代码中,const PCHAR 使用相当于const char* 呢

答案是否定的,原因很简单,typedef 是用来定义一种类型的新别名的,它不同于宏,不是简单的字符串替换,因此,const PCHAR 中的const 给予了整个指针本身常量性,也就是形成了常量指针,char *const 一个指向char 的常量指针。即它实际上相当于“charconst”,而不是“const char(指向常量 char 的指针)”。当然,要想让 const PCHAR 相当于 const char* 也很容易,如下面的代码所示:

1
2
typedef const char* PCHAR;
int strcmp(PCHAR, PCHAR);

其实,无论什么时候,只要为指针声明 typedef ,那么就应该在最终的typedef 名称中加一个const 以使得指针本身是常量

还需要特别注意的是,虽然typedef 并不真正影响对象的存储特性,但在语法上它还是一个存储类的关键字,就像auto extern static 和register 等关键字一样,因此,像下面这种声明是不可行的

1
typedef static int INT_STATIC;

不可行的原因是不能声明多个存储类关键字,由于 typedef 已经占据了存储类关键字的位置,因此,在 typedef 声明中就不能够再使用 static 或任何其他存储类关键字了。当然,编译器也会报错,如在 VC++2010 中的报错信息为“无法指定多个存储类”。

20221216-python3简单的串口通信实现

串口通信就是计算机和外设之间,通过数据信号线,底线、控制线等,按位进行传输数据的一种通讯方式,这种通讯方式使用的数据线少

串口是一种接口标准,它规定了接口的电气标准,没有规定接口插件电缆及使用的协议

python

20221216-导电斑马条

我们液晶连接中往往为节约空间会选择使用斑马条来进行连接,那么什么是斑马条的,下面给大家做个简单的介绍。导电橡胶连接器俗称为斑马条,由导电硅胶和绝缘硅胶交替分层叠加后硫化成型。导电橡胶连接器性能稳定可靠,生产装配简便且效率高。广泛用于游戏机、电话、电子表、计算器、仪表等产品的液晶显示器与电路板的连接。

如何确定斑马条大小呢:

1.长度设计:玻璃的长度-0.5mm ( 20 mm 以下依照原 LCD 玻璃长度 )

2.高度设计:LCD至PCB之间高度x(1.081.12)  —-压缩比8%12%

3. 宽度设计: LCD边缘宽度×(0.9 ~ 0.95)

4. 导电层宽度的选择: (Connector上的W’的尺寸)

5. Pitch之选择:以LCD或PCB上的Pitch ( 宽度) 上有2 ~ 3条Connector在上面为原则,越多条导电性越佳

20221216-单片机的烧写原理

单片机烧写,又称为单片机程序下载,烧录等,本质上是单片机和pc 机按照芯片厂家规定的编程协议,通过芯片厂家规定的接口,把已编译好的程序传输到单片机上,单片机把数据存储到自身存储器中的过程,在嵌入式系统中,常需要将代码或数据烧写至MCU内flash 或片外的norFlash 等
下面介绍几种芯片烧写方法

编程器烧写

使用专用编程器加上对应的适配座完成flash 烧写操作,要求芯片先烧写完成再焊接在目标板上,这种方式是最常用的,速度快,易上手是它的最大特点,

由于各种芯片不同封装,需要不同的适配座,这是客户需要考虑的投入

ISP 烧写

在系统编程(In-System Programming),通过MCU片内BootROM完成在板Flash烧写操作。要求芯片出厂时带有ISP固件,一般通过串行方式烧写,这种方式烧写速度很慢,不适用烧写容量大的代码场合。比如:LPC1700系列芯片,通过串口接口,使用上位机软件FlashMagic即可烧写,由于串行烧写速度慢,不适用于量产烧写的场合。

IAP 烧写

在应用编程中,IAP 支持用户程序运行时烧写flash 但要求部分用户程序已经烧写至mcu 片内 flash

调试接口烧写

由于Arm®内核的芯片越来越普及,借助仿真器通过MCU的调试接口(如JTAG/SWD等)控制MCU完成片内Flash甚至外扩的Flash烧写。这种方式芯片先焊接在板上再烧写,适用于产品维修升级,不需要多次焊接芯片,特别对于管脚数较大的芯片,多次焊接容易导致损坏;同样适用于封装怪异的MCU,如QFP144/QFN/HVQFN/BGA等。

通常我们把第一种称为离线烧写或者裸片烧写,另外的isp 烧写 iap 烧写,调试接口 烧写称为在线烧写,就是芯片已经安装至电路板的情况下,不需要将芯片从电路板取下即可完成flash 的烧写操作

理解这个原理需要知道几个知识点

单片机内部是由程序的,是出厂时固化在硬件中,用户无法修改的,这些程序可以调用各种通信接口,内部存储器等

可以下载的通信接口 :JTAG,SPI,UART,usb等;(还有很多可以扩展485、以太网等)

编程协议 一般大厂都会公开,在芯片的专用技术手册中会有

存储器:有很多种,掩膜,EPROM,EEROM,flash等寿命不一样,掩膜只能一次,而且要工厂做,flash擦写次数10000+;

可以这样比喻性的理解:单片机就是电脑的主板,我们写的程序就是操作系统,主板里面装入引导操作系统的基本程序,下载程序就是给电脑装系统!

20221215-使用wttr.in在你的终端中显示天气预报

wttr.in 是一个功能丰富的天气预报服务,它支持在命令行显示天气,它可以 根据你的ip 地址
自动检测你的位置,也支持指定位置或搜索地理位置,如城市,山区等,另外,你不需要安装它,你只需要使用curl 或wget

wttr.in 功能包括

显示当前天气以及3天内的天气预报,分为早晨,中午、傍晚和夜晚 包括温度范围、风速和风向 可见度,降水量 和概率

可以显示月相

基于你的ip地址自动检测位置

允许指定城市名称,3字母的机场代码,区域代码,GPS坐标,IP 地址或域名,你还可以指定地理位置,如湖泊 山脉,地标等

支持多语言位置名称 查询字符串必须以unicode 指定

支持指定天气预报显示的语言 它支持超过50种语言

来自美国的查询使用uscs 单位用于,世界其他地方使用公制系统,但你可以通过附加?u 使用uscs 附加?m 使用公制系统

3种输出格式,终端的ansi 浏览器的html 和png

或者你可以安装wego 这是一个使用wtter.in 的终端气候应用,虽然wego 要求注册一个api 密钥来安装

http://wttr.in 命令行示例
获取你所在位置的天气(http://wttr.in 会根据你的 IP 地址猜测你的位置):

curl wttr.in
通过在 curl 之后添加 -4,强制 cURL 将名称解析为 IPv4 地址(如果你用 IPv6 访问 wttr.in 有问题):

curl -4 wttr.in
如果你想检索天气预报保存为 png,还可以使用 Wget(而不是 cURL),或者你想这样使用它:

wget -O- -q wttr.in
如果相对 cURL 你更喜欢 Wget ,可以在下面的所有命令中用 wget -O- -q 替换 curl。

指定位置:

curl wttr.in/Dublin
显示地标的天气信息(本例中为艾菲尔铁塔):

curl wttr.in/~Eiffel+Tower
获取 IP 地址位置的天气信息(以下 IP 属于 GitHub):

curl wttr.in/@192.30.253.113
使用 USCS 单位检索天气:

curl wttr.in/Paris?u
如果你在美国,强制 http://wttr.in 使用公制系统(SI):

curl wttr.in/New+York?m
使用 Wget 将当前天气和 3 天预报下载为 PNG 图像:

wget wttr.in/Istanbul.png
你可以指定 PNG 的透明度,这在你要使用一个脚本自动添加天气信息到某些图片(比如墙纸)上有用。

对于其他示例,请查看wttr.in 项目页面或在终端中输入

curl wttr.in/:help

20221214-通过Content-Type和文件头判断文件类型

通过Content-Type和文件头判断文件类型

关于MIME

MIME的全称是Multipurpose Internet Mail Extensions,即多用途互联网邮件扩展,尽管读起来有些拗口,但大多数人可能都知道,
这是HTTP协议中用来定义文档性质及格式的标准。IETF RFC 6838,对HTTP传输内容类型进行了全面定义。
IANA(互联网号码分配机构)是负责管理所有标准MIME类型的官方机构。可以在这里)找到所有的标准MIME

服务器通过MIME告知响应内容类型,而浏览器则通过MIME类型来确定如何处理文档;
因此为传输内容(文档、图片等)设置正确的MIME非常重要

通常Server会在HTTP响应中设置Content-Type,如下面的响应:

1
CodeHTTP/1.1 200 OKServer: Golfe2    Content-Length: 233Content-Type: application/htmlDate: Sun, 28 Dec 2018 02:55:19 GMT12345

这表示服务端将返回html格式的文档,而同样客户端也可以在HTTP请求中设置Content-Type以告知服务器当前所发送内容的格式。
如下面的请求体:

1
CodePOST / HTTP/1.1Host: localhost:8000User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.9; rv:50.0) Gecko/20100101 Firefox/50.0Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8Connection: keep-aliveContent-Type: application/jsonContent-Length: 4651234567

这表示客户端会发送application/json格式的数据到服务端,同时应该注意到Accept请求头,这个选项用于告知服务器应该返回什么样的数据格式(由客户端接收并完成解析)。

MIME的格式

1
Codetype/subtype1

这是一个两级的分类,比较容易理解,第一级分类通常包含:

类型 描述
text 普通文本
image 某种图像
audio 某种音频文件
video 某种视频文件
application 应用数据
multi-part 复合内容

而二级类型则非常多,以下是一些常用的MIME:

MIME 描述
audio/wav wave音频流媒体文件
audio/webm webm 音频文件格式
audio/ogg ogg多媒体文件格式的音频文件
audio/mpeg mpeg多媒体文件格式的音频文件
image/gif gif图片
image/jpeg jpeg图片
image/png png图片
image/svg+xml svg矢量图片
application/json json格式
application/xml xml格式
application/xhtml+xml 扩展html格式
application/x-www-form-urlencoded 表单url内容编码
application/octet-stream 二进制格式
application/pdf pdf文档
application/atom+xml atom订阅feed流
multipart/form-data 多文档格式
text/plain 普通文本
text/html html文档
text/css css文件
text/javascript javascript文件
text/markdown markdown文档
video/mpeg mpeg多媒体视频文件
video/quicktime mov多媒体视频文件

MIME Type 与 Content-Type 的关系

首先看看tomcat服务器中默认的web.xml中的描述:

1
Code<!-- ===================== Default MIME Type Mappings =================== --><!-- When serving static resources, Tomcat will automatically generate    --><!-- a "Content-Type" header based on the resource's filename extension, --><!-- based on these mappings. Additional mappings can be added here (to --><!-- apply to all web applications), or in your own application's web.xml --><!-- deployment descriptor.   -->123456

再看看apache服务器中mime.types的描述:

1
CodeThis file controls what Internet media types are sent to the client forgiven file extension(s). Sending the correct media type to the clientis important so they know how to handle the content of the file.Extra types can either be added here or by using an AddType directivein your config files. For more information about Internet media types,please read RFC 2045, 2046, 2047, 2048, and 2077. The Internet media type registry is at http://www.iana.org/assignments/media-types/.123456

当web服务器收到静态的资源文件请求时,依据请求文件的后缀名在服务器的MIME配置文件中找到对应的MIME Type,再根据MIME Type设置HTTP Response的Content-Type,然后浏览器根据Content-Type的值处理文件。

也就是说, 文件扩展名=>MIME Type=>Content-Type

通过文件头识别文件

不同的文件类型有不同的文件头, 而文件头部的几个字节被称为Magic Number, 通常用十六进制表示, 可用来判断文件类型.

比如png文件的文件头Magic Number是0x89504E开始的, java class文件Magic Number为Oxcafebabe

我们可以通过判断文件的文件头信息来判断文件的类型, 而且即使改变文件扩展名文件头信息也是不改变的.

通过java代码判断文件类型:

1
Javapublic class FileType {    //默认判断文件头前三个字节内容    public static int CHECK_BYTES_NUMBER = 3;    public final static Map<String, String> FILE_TYPE_MAP = new HashMap<String, String>();    private FileType(){}    static{        getAllFileType(); //初始化文件类型信息    }    /**     * Discription:[getAllFileType,常见文件头信息]     */    private static void getAllFileType()    {        FILE_TYPE_MAP.put("ffd8ffe000104a464946", "jpg"); //JPEG (jpg)        FILE_TYPE_MAP.put("89504e470d0a1a0a0000", "png"); //PNG (png)        FILE_TYPE_MAP.put("47494638396126026f01", "gif"); //GIF (gif)        FILE_TYPE_MAP.put("49492a00227105008037", "tif"); //TIFF (tif)        FILE_TYPE_MAP.put("424d228c010000000000", "bmp"); //16色位图(bmp)        FILE_TYPE_MAP.put("424d8240090000000000", "bmp"); //24位位图(bmp)        FILE_TYPE_MAP.put("424d8e1b030000000000", "bmp"); //256色位图(bmp)        FILE_TYPE_MAP.put("41433130313500000000", "dwg"); //CAD (dwg)        FILE_TYPE_MAP.put("3c21444f435459504520", "html"); //HTML (html)        FILE_TYPE_MAP.put("3c21646f637479706520", "htm"); //HTM (htm)        FILE_TYPE_MAP.put("48544d4c207b0d0a0942", "css"); //css        FILE_TYPE_MAP.put("696b2e71623d696b2e71", "js"); //js        FILE_TYPE_MAP.put("7b5c727466315c616e73", "rtf"); //Rich Text Format (rtf)        FILE_TYPE_MAP.put("38425053000100000000", "psd"); //Photoshop (psd)        FILE_TYPE_MAP.put("46726f6d3a203d3f6762", "eml"); //Email [Outlook Express 6] (eml)        FILE_TYPE_MAP.put("d0cf11e0a1b11ae10000", "doc"); //MS Excel 注意:word、msi 和 excel的文件头一样        FILE_TYPE_MAP.put("d0cf11e0a1b11ae10000", "vsd"); //Visio 绘图        FILE_TYPE_MAP.put("5374616E64617264204A", "mdb"); //MS Access (mdb)        FILE_TYPE_MAP.put("252150532D41646F6265", "ps");        FILE_TYPE_MAP.put("255044462d312e350d0a", "pdf"); //Adobe Acrobat (pdf)        FILE_TYPE_MAP.put("2e524d46000000120001", "rmvb"); //rmvb/rm相同        FILE_TYPE_MAP.put("464c5601050000000900", "flv"); //flv与f4v相同        FILE_TYPE_MAP.put("00000020667479706d70", "mp4");        FILE_TYPE_MAP.put("49443303000000002176", "mp3");        FILE_TYPE_MAP.put("000001ba210001000180", "mpg"); //        FILE_TYPE_MAP.put("3026b2758e66cf11a6d9", "wmv"); //wmv与asf相同        FILE_TYPE_MAP.put("52494646e27807005741", "wav"); //Wave (wav)        FILE_TYPE_MAP.put("52494646d07d60074156", "avi");        FILE_TYPE_MAP.put("4d546864000000060001", "mid"); //MIDI (mid)        FILE_TYPE_MAP.put("504b0304140000000800", "zip");        FILE_TYPE_MAP.put("526172211a0700cf9073", "rar");        FILE_TYPE_MAP.put("235468697320636f6e66", "ini");        FILE_TYPE_MAP.put("504b03040a0000000000", "jar");        FILE_TYPE_MAP.put("4d5a9000030000000400", "exe");//可执行文件        FILE_TYPE_MAP.put("3c25402070616765206c", "jsp");//jsp文件        FILE_TYPE_MAP.put("4d616e69666573742d56", "mf");//MF文件        FILE_TYPE_MAP.put("3c3f786d6c2076657273", "xml");//xml文件        FILE_TYPE_MAP.put("494e5345525420494e54", "sql");//xml文件        FILE_TYPE_MAP.put("7061636b616765207765", "java");//java文件        FILE_TYPE_MAP.put("406563686f206f66660d", "bat");//bat文件        FILE_TYPE_MAP.put("1f8b0800000000000000", "gz");//gz文件        FILE_TYPE_MAP.put("6c6f67346a2e726f6f74", "properties");//bat文件        FILE_TYPE_MAP.put("cafebabe0000002e0041", "class");//bat文件        FILE_TYPE_MAP.put("49545346030000006000", "chm");//bat文件        FILE_TYPE_MAP.put("04000000010000001300", "mxp");//bat文件        FILE_TYPE_MAP.put("504b0304140006000800", "docx");//docx文件        FILE_TYPE_MAP.put("d0cf11e0a1b11ae10000", "wps");//WPS文字wps、表格et、演示dps都是一样的        FILE_TYPE_MAP.put("6431303a637265617465", "torrent");        FILE_TYPE_MAP.put("6D6F6F76", "mov"); //Quicktime (mov)        FILE_TYPE_MAP.put("FF575043", "wpd"); //WordPerfect (wpd)        FILE_TYPE_MAP.put("CFAD12FEC5FD746F", "dbx"); //Outlook Express (dbx)        FILE_TYPE_MAP.put("2142444E", "pst"); //Outlook (pst)        FILE_TYPE_MAP.put("AC9EBD8F", "qdf"); //Quicken (qdf)        FILE_TYPE_MAP.put("E3828596", "pwl"); //Windows Password (pwl)        FILE_TYPE_MAP.put("2E7261FD", "ram"); //Real Audio (ram)    }    /**     * 根据制定文件的文件头判断其文件类型     * @param filePaht     * @return     */    public static String getFileType(String filePaht){        String res = null;        try {            FileInputStream is = new FileInputStream(filePaht);            getFileType(is);        } catch (FileNotFoundException e) {            e.printStackTrace();        }        return res;    }    public static String getFileType(InputStream in){        String res = null;        try {            byte[] b = new byte[CHECK_BYTES_NUMBER];            in.read(b, 0, b.length);            String fileCode = bytesToHexString(b);//            System.out.println(fileCode);            //这种方法在字典的头代码不够位数的时候可以用但是速度相对慢一点            Iterator<String> keyIter = FILE_TYPE_MAP.keySet().iterator();            while(keyIter.hasNext()){                String key = keyIter.next();                if(key.toLowerCase().startsWith(fileCode.toLowerCase()) || fileCode.toLowerCase().startsWith(key.toLowerCase())){                    res = FILE_TYPE_MAP.get(key);                    break;                }            }        } catch (FileNotFoundException e) {            e.printStackTrace();        } catch (IOException e) {            e.printStackTrace();        }        return res;    }    /**     * 得到上传文件的文件头     * @param src     * @return     */    public static String bytesToHexString(byte[] src) {        StringBuilder stringBuilder = new StringBuilder();        if (src == null || src.length <= 0) {            return null;        }        for (int i = 0; i < src.length; i++) {            int v = src[i] & 0xFF;            String hv = Integer.toHexString(v);            if (hv.length() < 2) {                stringBuilder.append(0);            }            stringBuilder.append(hv);        }        return stringBuilder.toString();    }    public static int getCheckBytesNumber() {        return CHECK_BYTES_NUMBER;    }    public static void setCheckBytesNumber(int checkBytesNumber) {        CHECK_BYTES_NUMBER = checkBytesNumber;    }}123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150

常见文件头表示如下:

255044PDF
526563 EML
D0CF11 PPT
4D5AEE COM
E93B03 COM
4D5A90 EXE
424D3E BMP
49492A TIF
384250 PSD
C5D0D3 EPS
0A0501 PCS
89504E PNG
060500 RAW
000002 TGA
60EA27 ARJ
526172 RAR
504B03 ZIP
495363 CAB
1F9D8C Z
524946 WAV
435753 SWF
3026B2 WMV
3026B2 WMA
2E524D RM
00000F MOV
000077 MOV
000001 MPA
FFFB50 MP3
234558 m3u
3C2144 HTM
FFFE3C XSL
3C3F78 XML
3C3F78 MSC
4C0000 LNK
495453 CHM
805343 scm
D0CF11 XLS
31BE00 WRI
00FFFF MDF
4D4544 MDS
5B436C CCD
00FFFF IMG
FFFFFF SUB
17A150 PCB
2A5052 ECO
526563 PPC
000100 DDB
42494C LDB
2A7665 SCH
2A2420 LIB
434841 FNT
7B5C72 RTF
7B5072 GTD
234445 PRG
000007 PJT
202020 BAS
000002 TAG
4D5A90 dll
4D5A90 OCX
4D5A50 DPL
3F5F03 HLP
4D5A90 OLB
4D5A90 IMM
4D5A90 IME
3F5F03 LHP
C22020 NLS
5B5769 CPX
4D5A16 DRV
5B4144 PBK
24536F PLL
4E4553 NES
87F53E GBC
00FFFF SMD
584245 XBE
005001 XMV
000100 TTF
484802 PDG
000100 TST
414331 dwg
D0CF11 max

另外还有一些重要的文件,没有固定的文件头,如下:

TXT 没固定文件头定义
TMP 没固定文件头定义
INI 没固定文件头定义
BIN 没固定文件头定义
DBF 没固定文件头定义
C 没没固定文件头定义
CPP 没固定文件头定义
H 没固定文件头定义
BAT 没固定文件头定义

还有一些不同的文件有相同的文件头,最典型的就是下面:

4D5A90 EXE
4D5A90 dll
4D5A90 OCX
4D5A90 OLB
4D5A90 IMM
4D5A90 IME

文件上传

当我们需要实现上传文件的时候, 为了安全起见, 我们需要判断上传文件的格式, 防止将病毒木马等有害的文件上传到服务器上.

判断文件类型的三种方式

  • 通过文件后缀名

    这个方法只要修改后缀名就可以了

  • 通过Content-Type判断

    但是Content-Type取决于文件类型, 文件类型取决于文件扩展名, 所以改变了文件扩展名就改变了Content-Type

  • 通过文件头判断文件, 即使文件扩展名改变了文件头也不会改变

文件上传的思路: 先判断Content-Type, Content-Type符合条件的再判断文件头信息

1
Java@ResponseBody    @GetMapping("validate")    public Map<String, String> validate(@Validated({AllFiled.class}) UserInfo userInfo, BindingResult result){//        SpringValidatorAdapter adapter = (SpringValidatorAdapter)result;        Map<String, String> map = new HashMap<String, String>();        if (result.hasErrors()) {            List<ObjectError> list  = result.getAllErrors();            for (ObjectError error :                    list) {                FieldError fieldError = (FieldError)error;                String defaultMessage = fieldError.getDefaultMessage();                String field = fieldError.getField();                map.put(field, defaultMessage);            }        }        return map;    }//    consumes = {//        MediaType.MULTIPART_FORM_DATA_VALUE }, produces = MediaType.TEXT_PLAIN_VALUE    @PostMapping(value = "file")    @ResponseBody    public String file(@RequestParam("username") String name, MultipartFile file) throws IOException {        //获取文件名        String fileName = file.getOriginalFilename();        //获取表单提交文件使用的字段        String partName = file.getName();        //判断文件是否为空        boolean empty = file.isEmpty();        //获取ContentType        String contentType = file.getContentType();        //获取文件直接数        Long size = file.getSize();        //获取文件所有字节        byte[] bytes = file.getBytes();        //获取InputStream        InputStream in = file.getInputStream();        //根据文件头获取文件类型        String type = FileType.getFileType(in);                //业务.....        StringBuilder builder = new StringBuilder();        //存储文件        File root = new File("D:/temp");        if (!root.isDirectory()) {            root.mkdirs();        }        try {            file.transferTo(new File(root, name));            return String.format("Upload to %s", fileName);        } catch (IllegalStateException e) {            e.printStackTrace();        } catch (IOException e) {            e.printStackTrace();        }        return "Upload Failed";    }

20221214-二维码版本与容量

二维码 QR - 版本与容量

版本 矩阵大小 纠错等级 八位字节 数字 字母数字
1 21 L 17 41 25
M 14 34 20
Q 11 27 16
H 7 17 10
2 25 L 32 77 47
M 26 63 38
Q 20 48 29
H 14 34 20
3 29 L 53 127 77
M 42 101 61
Q 32 77 47
H 24 58 35
4 33 L 78 187 114
M 62 149 90
Q 46 111 67
H 34 82 50
5 37 L 106 255 154
M 84 202 122
Q 60 144 87
H 44 106 64
6 41 L 134 322 195
M 106 255 154
Q 74 178 108
H 58 139 84
7 45 L 154 370 224
M 122 293 178
Q 86 207 125
H 64 154 93
8 49 L 192 461 279
M 152 365 221
Q 108 259 157
H 84 202 122
9 53 L 230 552 335
M 180 432 262
Q 130 312 189
H 98 235 143
10 57 L 271 652 395
M 213 513 311
Q 151 364 221
H 119 288 174

20221214-PDF文本的基本结构

PDF 文件是可以有效随机存取和增量更新的,根据基本规则,一个PDF 文件由四部分组成

  1. header部分:占一行,标识PDF规范的版本;
  2. body部分:包含PDF文件中的所有obj对象;
  3. cross-reference table 部分:交叉引用表,包含文件中间接对象的信息;
  4. trailer部分:包含交叉引用表和文件正文中某些特殊对象的位置;

按照惯例,PDF 文件中的标记按行排列,每行的终止标记可以是回车、换行、或两者兼有,包含二进制数据的PDF 文件可以有任意长的行

为了提高PDF 文件的兼容性,不属于流对象的数据行限制为不超过255个字符,但有一个例外,签名字典的内容字符串不受行长度限制

20221214-PNG格式详解

概述

png 是20世纪90年代开始开发的图像文件存储格式,其目的是替代gif 和tiff 文件格式,同时增加一些gif 文件格式所不具备的特性,流式网络图形格式名称来源于非官方的 是一种位图文件存储格式,读成ping 。png 用来存储灰度图像时,灰度图像的深度可多到16位,存储彩色图像时,彩色图像的深度可多到48位,并且还可存储多到16位的alpha 通道,png 使用从LZ77 派生的无损数据压缩算法

特性

使用调色板技术可支持256种颜色的彩色图像 必须的

流式读 写性,图像文件格式允许连续读出和写入图像数据 因此适于网络传播

逐次逼近显示,这种特性可使在通信链路上传输图像文件的同时就在终端上显示图像,把整个轮廓显示出来之后逐步显示图像的细节,也就是先用低分辨率显示图像,然后逐步提高它的分辨率 类似马赛克逐渐消除的过程

透明性,这个性能可使图像中某些部分不显示出来,用来创建一些有特色的图像

辅助信息,这个特性可用来在图像文件中存储一些文本注释信息 就是可以说一些废话

独立于计算机硬件环境

使用无损压缩

可在一个文件中存储多幅图像

文件结构

png 图像格式文件由文件署名和数据块 组成

文件署名域

8字节的png 文件署名域用来识别该文件是不是png 文件,该域的值是

十进制数 十六进制数
137 89
80 50
78 4e
71 47
13 0d
10 0a
26 1a
10 0a
这个文件署名就是在《利用文件头标志判断文件类型》中提到的文件头标志了,很简单。

数据块

这里有两种类型的数据块,一种是称为关键数据块,就是必须要有的块,另一种叫做辅助数据块

每个数据块都由下表所示的4个域组成

名称 字节数 说明
Length(长度) 4字节 指定数据块中数据域的长度,其长度不超过(231−1)(231−1)字节
Chunk Type Code(数据块类型码) 4字节 数据块类型码由ASCII字母(A-Z和a-z)组成
Chunk Data(数据块实际内容 可变长度 存储按照Chunk Type Code指定的数据
CRC(循环冗余检测 4字节 存储用来检测是否有错误的循环冗余码

其中CRC 域中的值是对chunk type code 域和chunk data 域中的数据进行计算得到的,可以看作一种校验码

关键数据块

关键数据块中的4个标准数据块是

文件头数据块 IHDR

它包含有png 文件中存储的图像数据的基本信息,并要作为第一个数据块出现在png 数据流中,而且一个png 数据流中只能有一个文件头数据块

文件头数据块由13字节,组成结构如下

域的名称 字节数 说明
Width 4 bytes 图像宽度,以像素为单位
Height 4 bytes 图像高度,以像素为单位
Bit depth 1 byte 图像深度:索引彩色图像:1,2,4或8 ;灰度图像:1,2,4,8或16 ;真彩色图像:8或16
ColorType 1 byte 颜色类型:0:灰度图像, 1,2,4,8或16;2:真彩色图像,8或16;3:索引彩色图像,1,2,4或84:带α通道数据的灰度图像,8或16;6:带α通道数据的真彩色图像,8或16
Compression method 1 byte 压缩方法(LZ77派生算法)
Filter method 1 byte 滤波器方法
Interlace method 1 byte 隔行扫描方法:0:非隔行扫描;1: Adam7(由Adam M. Costello开发的7遍隔行扫描方法)

调色板数据块PLTE

它包含有与索引彩色图像相关的彩色变换数据,它仅与索引彩色图像有关,而且要放在图像数据块之前,真彩色的png 数据流也可以有调色板数据块,目的是便于非真彩色显示程序用它来量化图像数据,从而显示该图像,结构如下

|颜色|字节|意义|
|Red|1 byte | 0 = 黑色, 255 = 红|
|Green|1 byte | 0 = 黑色, 255 = 绿色|
|Blue|1 byte | 0 = 黑色, 255 = 蓝色|

PLTE 数据块是定义图像的调色板信息,PLTE 可以包含1-256 个调色板信息,每个调色板信息由3个字节组成,因此调色板数据块所包含的最大字节数为768 调色板的长度应该是3的倍数,否则,这将使一个非法的调色板

对于索引图像,调色板信息是必须的,调色板的颜色索引从0开始编号,然后是 1 2 调色板的颜色数不能超过色深中规定的颜色数,否则,这将导致png 图像不合法

图像数据块 IDAT

它存储实际的数据,在数据流中可包含多个连续顺序的图像数据块
IDAT 存放着图像真正的数据信息,因此,如果能够了解IDAT 的结构,我们就可以很方便的生成PNG 图像

图像结束数据IEND

它用来标记png 文件或者数据流已经结束,并且必须要放在文件的尾部

如果我们仔细观察PNG文件,我们会发现,文件的结尾12个字符看起来总应该是这样的:

00 00 00 00 49 45 4E 44 AE 42 60 82

不难明白,由于数据块结构的定义,IEND 数据块的长度总是0 0000000除非认为加入信息,数据标识总是iend 因此,CRC码也总是AE 42 60 82

最后,除了表示数据块开始IHDR 必须放在最前面,表示png 文件结束的iend 数据块放在最后面,其他数据块的存放顺序没有限制

辅助数据块

比较杂 ,不需要全部了解透

PNG 文件格式规范指定的10个辅助数据块是

背景颜色数据块 bKGD

基色和白色度数块cHRM 所谓白色度是指当 R G B 最大值时在显示器上产生的白色度

图像gamma 数据块 gAMA

图像直方图数据块hIST
物理像素尺寸数据块 pHYs

样本有效位数据块 sBIT

文本信息数据块 tEXt
图像最后修改时间数据块 tIME
图像透明数据块 tRNS

压缩文本数据块 zTXt

数据块摘要

关键数据块,辅助数据块和专用公共数据块 综合下表中

数据块符号 数据块名称 多数据块 可选否 位置限制
IHDR 文件头数据块 第一块
cHRM 基色和白色点数据块 在PLTE和IDAT之前
gAMA 图像γ数据块 在PLTE和IDAT之前
sBIT 样本有效位数据块 在PLTE和IDAT之前
PLTE 调色板数据块 在IDAT之前
bKGD 背景颜色数据块 在PLTE之后IDAT之前
hIST 图像直方图数据块 在PLTE之后IDAT之前
tRNS 图像透明数据块 在PLTE之后IDAT之前
oFFs (专用公共数据块) 在IDAT之前
pHYs 物理像素尺寸数据块 在IDAT之前
sCAL (专用公共数据块) 在IDAT之前
IDAT 图像数据块 与其他IDAT连续
tIME 图像最后修改时间数据块 无限制
tEXt 文本信息数据块 无限制
zTXt 压缩文本数据块 无限制
fRAc (专用公共数据块) 无限制
gIFg (专用公共数据块) 无限制
gIFt (专用公共数据块) 无限制
gIFx (专用公共数据块) 无限制
IEND 图像结束数据 最后一个数据块

tEXt和zTXt数据块中的标准关键字:

关键字 说明
Title 图像名称或者标题
Author 图像作者名
Description 图像说明
Copyright 版权声明
CreationTime 原图创作时间
Software 创作图像使用的软件
Disclaimer 弃权
Warning 图像内容警告
Source 创作图像使用的设备
Comment 各种注释
一个例子

  • Copyrights © 2015-2024 TeX_baitu
  • 访问人数: | 浏览次数:

请我喝杯咖啡吧~