Js 计算文件MD5的两种方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
// 方式1:使用crypto-js
import CryptoJSMD5 from 'crypto-js/md5'
import CryptoJSEncLatin1 from 'crypto-js/enc-latin1'
import CryptoJSEncHex from 'crypto-js/enc-hex'
getFileMD5(file) {
const start = new Date().getTime()
return new Promise((resolve) => {
const fileReader = new FileReader()
fileReader.onloadend = (ev) => {
const md5 = CryptoJSMD5(
CryptoJSEncLatin1.parse(ev.target.result)
).toString(CryptoJSEncHex)
console.log(
`计算MD5, file=${file.name}, size=${file.size}, md5=${md5}, elapsed=${
new Date().getTime() - start
}ms`
)
resolve(md5)
}
fileReader.readAsBinaryString(file)
})
},

// 方式2:使用spark-md5
import SparkMD5 from 'spark-md5'
getFileMD5(file) {
const start = new Date().getTime()
return new Promise((resolve) => {
const fileReader = new FileReader()
fileReader.onloadend = (ev) => {
const md5 = SparkMD5.hashBinary(ev.target.result)
console.log(
`计算MD5, file=${file.name}, size=${file.size}, md5=${md5}, elapsed=${
new Date().getTime() - start
}ms`
)
resolve(md5)
}
fileReader.readAsBinaryString(file)
})
},

实测使用spark-md5计算要快一些

使用div画table

网上找到的两种方法,先记录下备用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
<!DOCTYPE html>
<html>
<head>
<style>
.table-tr {
width: 1200px;
}
.table-td,.table-tdRowEnd,.table-tdLastRow,.table-tdLastRowEnd{
float: left;
width: 198px;
height: 30px;
line-height: 30px;
border-style: solid;
}
.table-td {
border-width: 1px 0px 0px 1px;
}
.table-tdRowEnd {
border-width: 1px 1px 0px 1px;
}
.table-tdLastRow {
border-width: 1px 0px 1px 1px;
}
.table-tdLastRowEnd {
border-width: 1px 1px 1px 1px;
}
</style>
</head>
<body>
<div class="table-tr" >
<div class="table-td">姓名</div>
<div class="table-td">年龄</div>
<div class="table-td">电话</div>
<div class="table-td">QQ号</div>
<div class="table-td">邮箱地址</div>
<div class="table-tdRowEnd">主页</div>
</div>

<div class="table-tr">
<div class="table-td">小明</div>
<div class="table-td">18</div>
<div class="table-td">123456789</div>
<div class="table-td">123456</div>
<div class="table-td">admin@admin.com</div>
<div class="table-tdRowEnd">feiniaomy.com</div>
</div>
<div class="table-tr" >
<div class="table-td">小红</div>
<div class="table-td">20</div>
<div class="table-td">123456789</div>
<div class="table-td">654321</div>
<div class="table-td">xxxx@xx.com</div>
<div class="table-tdRowEnd">baidu.com</div>
</div>
<div class="table-tr" >
<div class="table-tdLastRow">小蓝</div>
<div class="table-tdLastRow">20</div>
<div class="table-tdLastRow">null</div>
<div class="table-tdLastRow">null</div>
<div class="table-tdLastRow"></div>
<div class="table-tdLastRowEnd"></div>
</div>
</body>
</html>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
<!DOCTYPE html>
<html>
<head>
<style>
ul{margin:0;padding:0;list-style:none;}
.table{display:table;border-collapse:collapse;border:1px solid #ccc;}
.table-caption{display:table-caption;margin:0;padding:0;font-size:16px;}
.table-column-group{display:table-column-group;}
.table-column{display:table-column;width:200px;}
.table-row-group{display:table-row-group;}
.table-row{display:table-row;}
.table-row-group .table-row:hover,.table-footer-group .table-row:hover{background:#f6f6f6;}
.table-cell{display:table-cell;padding:5px;border:1px solid #ccc;}
.table-header-group{display:table-header-group;background:#eee;font-weight:bold;}
.table-footer-group{display:table-footer-group;}
</style>
</head>
<body>
<div class="table">
<div class="table-column-group">
<div class="table-column"></div>
<div class="table-column"></div>
<div class="table-column"></div>
</div>
<div class="table-header-group">
<ul class="table-row">
<li class="table-cell">序号</li>
<li class="table-cell">姓名</li>
<li class="table-cell">年龄</li>
</ul>
</div>
<div class="table-row-group">
<ul class="table-row">
<li class="table-cell">1</li>
<li class="table-cell">小明</li>
<li class="table-cell">19</li>
</ul>
<ul class="table-row">
<li class="table-cell">2</li>
<li class="table-cell">小红</li>
<li class="table-cell">21</li>
</ul>
<ul class="table-row">
<li class="table-cell">3</li>
<li class="table-cell">小蓝</li>
<li class="table-cell">26</li>
</ul>
</div>
<div class="table-footer-group">
<ul class="table-row">
<li class="table-cell">底部</li>
<li class="table-cell">底部</li>
<li class="table-cell">底部</li>
</ul>
</div>
</div>
</body>
</html>

RSA 公钥和私钥生成

私钥生成

1
openssl genrsa -out rsa_private.pem 1024

注意:这里生成的是 PKCS1 格式的文件,也称之为传统的私钥格式。

生成公钥

1
openssl rsa -in rsa_private.pem -out rsa_public.pem -pubout

格式转换

把 RSA 私钥转 PKCS1 转换为 PKCS8 格式,执行如下:

1
openssl pkcs8 -topk8 -inform PEM -in rsa_private.pem -outform PEM -nocrypt -out rsa_private_pkcs8.pem

把 RSA 私钥 PKCS8 格式转换为 PKCS1 格式,执行如下:

1
openssl rsa -in rsa_private_pkcs8.pem -out pkcs1.pem

参考:https://veryitman.com/2019/05/11/macOS-%E7%94%9F%E6%88%90-RSA-%E5%85%AC%E9%92%A5%E5%92%8C%E7%A7%81%E9%92%A5/

Java生成GBK所有字符,以及判断字符是否是GBK编码内字符

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
/**
* 参见文档: <a href="https://zh.wikipedia.org/wiki/%E6%B1%89%E5%AD%97%E5%86%85%E7%A0%81%E6%89%A9%E5%B1%95%E8%A7%84%E8%8C%83">汉字内码扩展规范</a>
* <p>
* GBK/1:GB2312非汉字符号: A1~A9, A1~FE
* GBK/2:GB2312汉字: B0~F7, A1~FE
* GBK/3:扩充汉字: 81~A0, 40~FE (7F除外)
* GBK/4:扩充汉字: AA~FE, 40~A0 (7F除外)
* GBK/5:扩充非汉字: A8~A9, 40~A0 (7F除外)
*
* @author Gao Youbo
* @since 2022-08-23 16:34
*/
public class GBK {
private static final Charset CHARSET = Charset.forName("GBK");

/**
* 获取所有GBK编码字符
*/
public static List<String> getGBK() {
List<String> words = new ArrayList<>();
byte[] bytes = new byte[2];

// GBK/1:GB2312非汉字符号
for (int b1 = 0xA1; b1 <= 0xA9; b1++) {
bytes[0] = (byte) b1;
for (int b2 = 0xA1; b2 <= 0xFE; b2++) {
bytes[1] = (byte) b2;
words.add(new String(bytes, CHARSET));
}
}

// GBK/2:GB2312汉字
for (int b1 = 0xB0; b1 <= 0xF7; b1++) {
bytes[0] = (byte) b1;
for (int b2 = 0xA1; b2 <= 0xFE; b2++) {
bytes[1] = (byte) b2;
words.add(new String(bytes, CHARSET));
}
}

// GBK/3:扩充汉字
for (int b1 = 0x81; b1 <= 0xA0; b1++) {
bytes[0] = (byte) b1;
for (int b2 = 0x40; b2 <= 0xFE; b2++) {
bytes[1] = (byte) b2;
if (b2 != 0x7F) {
words.add(new String(bytes, CHARSET));
}
}
}

// GBK/4:扩充汉字
for (int b1 = 0xAA; b1 <= 0xFE; b1++) {
bytes[0] = (byte) b1;
for (int b2 = 0x40; b2 <= 0xA0; b2++) {
bytes[1] = (byte) b2;
if (b2 != 0x7F) {
words.add(new String(bytes, CHARSET));
}
}
}

// GBK/5:扩充非汉字
for (int b1 = 0xA8; b1 <= 0xA9; b1++) {
bytes[0] = (byte) b1;
for (int b2 = 0x40; b2 <= 0xA0; b2++) {
bytes[1] = (byte) b2;
if (b2 != 0x7F) {
words.add(new String(bytes, CHARSET));
}
}
}

return words;
}

public static boolean isGBK(String str) {
boolean isGBK = false;
char[] chars = str.toCharArray();
for (char c : chars) {
byte[] bytes = String.valueOf(c).getBytes(CHARSET);
if (bytes.length == 2) { // GBK 编码为两个字节
int b1 = bytes[0] & 0xff;
int b2 = bytes[1] & 0xff;
if (b1 >= 0xA1 && b1 <= 0xA9 && b2 >= 0xA1 & b2 <= 0xFE) {
isGBK = true;
break;
}

if (b1 >= 0xB0 && b1 <= 0xF7 && b2 >= 0xA1 & b2 <= 0xFE) {
isGBK = true;
break;
}

if (b1 >= 0x81 && b1 <= 0xA0 && b2 >= 0x40 & b2 <= 0xFE && b2 != 0x7F) {
isGBK = true;
break;
}

if (b1 >= 0xAA && b1 <= 0xFE && b2 >= 0x40 & b2 <= 0xA0 && b2 != 0x7F) {
isGBK = true;
break;
}

if (b1 >= 0xA8 && b1 <= 0xA9 && b2 >= 0x40 & b2 <= 0xA0 && b2 != 0x7F) {
isGBK = true;
break;
}
}
}
return isGBK;
}
}

Java实现Base128编码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
/**
* @author Gao Youbo
* @since 2022-08-19 10:28
*/
public class Base128 {
private static final char[] DEFAULT_CHAR_TABLE = {
'0', '1', '2', '3', '4', '5', '6', '7',
'8', '9', 'A', 'B', 'C', 'D', 'E', 'F',
'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N',
'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V',
'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd',
'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l',
'm', 'n', 'o', 'p', 'q', 'r', 's', 't',
'u', 'v', 'w', 'x', 'y', 'z', '-', '_',

'α', 'β', 'γ', 'δ', 'ε', 'ζ', 'η', 'θ',
'ι', 'κ', 'λ', 'μ', 'ν', 'ξ', 'ο', 'π',
'ρ', 'ς', 'σ', 'τ', 'υ', 'φ', 'χ', 'ψ',
'ω', 'à', 'á', 'â', 'ã', 'ä', 'å', 'æ',
'ç', 'è', 'é', 'ê', 'ë', 'ì', 'í', 'î',
'ï', 'ð', 'ñ', 'ò', 'ó', 'ô', 'õ', 'ö',
'÷', 'ø', 'ù', 'ú', 'û', 'ü', 'ý', 'þ',
'ÿ', 'Ā', 'ā', 'Ă', 'ă', 'Ą', 'ą', 'Ć',
};

private final char[] charTable;
private final HashMap<Character, Integer> indexTable;

public Base128() {
this(DEFAULT_CHAR_TABLE);
}

public Base128(char[] charTable) {
this.charTable = charTable;
this.indexTable = new HashMap<>(charTable.length);
for (int j = 0; j < charTable.length; j++) {
indexTable.put(charTable[j], j);
}
}

private String encodeToString(byte[] data) {
if (data == null || data.length == 0) {
return "";
}
StringBuilder sb = new StringBuilder();
int tail = 0;
for (int i = 0; i < data.length; i++) {
int mov = (i % 7 + 1);
int curr = 0xFF & data[i];
int code = tail + (curr >> mov);
sb.append(charTable[code]);
tail = (0xFF & (curr << (8 - mov))) >> 1;
if (mov == 7) {
sb.append(charTable[tail]);
tail = 0;
}
}
sb.append(charTable[tail]);
return sb.toString();
}

private byte[] decode(String str) {
if (StringUtils.isBlank(str)) {
return new byte[]{};
}
int length = (int) Math.floor(str.length() * 0.875);
byte[] result = new byte[length];
int idx = 0;
int head = indexTable.get(str.charAt(0)) << 1;
for (int i = 1; i < str.length(); ) {
int mod = i % 8;
int code = indexTable.get(str.charAt(i++));
result[idx++] = (byte) (0xFF & (head + (code >> (7 - mod))));
if (mod == 7) {
head = 0xFF & (indexTable.get(str.charAt(i++)) << 1);
} else {
head = 0xFF & (code << (mod + 1));
}
}
return result;
}
}

下面提供一份更全的编码字节表,使用这个表可以实现Base256等

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
private static final char[] symbolTable = {
'0', '1', '2', '3', '4', '5', '6', '7',
'8', '9', 'A', 'B', 'C', 'D', 'E', 'F',
'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N',
'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V',
'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd',
'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l',
'm', 'n', 'o', 'p', 'q', 'r', 's', 't',
'u', 'v', 'w', 'x', 'y', 'z', '-', '_',

'α', 'β', 'γ', 'δ', 'ε', 'ζ', 'η', 'θ',
'ι', 'κ', 'λ', 'μ', 'ν', 'ξ', 'ο', 'π',
'ρ', 'ς', 'σ', 'τ', 'υ', 'φ', 'χ', 'ψ',
'ω', 'à', 'á', 'â', 'ã', 'ä', 'å', 'æ',
'ç', 'è', 'é', 'ê', 'ë', 'ì', 'í', 'î',
'ï', 'ð', 'ñ', 'ò', 'ó', 'ô', 'õ', 'ö',
'÷', 'ø', 'ù', 'ú', 'û', 'ü', 'ý', 'þ',
'ÿ', 'Ā', 'ā', 'Ă', 'ă', 'Ą', 'ą', 'Ć',

'ć',
'Ĉ', 'ĉ', 'Ċ', 'ċ', 'Č', 'č', 'Ď', 'ď',
'Đ', 'đ', 'Ē', 'ē', 'Ĕ', 'ĕ', 'Ė', 'ė',
'Ę', 'ę', 'Ě', 'ě', 'Ĝ', 'ĝ', 'Ğ', 'ğ',
'Ġ', 'ġ', 'Ģ', 'ģ', 'Ĥ', 'ĥ', 'Ħ', 'ħ',
'Ĩ', 'ĩ', 'Ī', 'ī', 'Ĭ', 'ĭ', 'Į', 'į',
'İ', 'ı', 'IJ', 'ij', 'Ĵ', 'ĵ', 'Ķ', 'ķ',
'ĸ', 'Ĺ', 'ĺ', 'Ļ', 'ļ', 'Ľ', 'ľ', 'Ŀ',
'ŀ', 'Ł', 'ł', 'Ń', 'ń', 'Ņ', 'ņ', 'Ň',
'ň', 'ʼn', 'Ŋ', 'ŋ', 'Ō', 'ō', 'Ŏ', 'ŏ',
'Ő', 'ő', 'Œ', 'œ', 'Ŕ', 'ŕ', 'Ŗ', 'ŗ',
'Ř', 'ř', 'Ś', 'ś', 'Ŝ', 'ŝ', 'Ş', 'ş',
'Š', 'š', 'Ţ', 'ţ', 'Ť', 'ť', 'Ŧ', 'ŧ',
'Ũ', 'ũ', 'Ū', 'ū', 'Ŭ', 'ŭ', 'Ů', 'ů',
'Ű', 'ű', 'Ų', 'ų', 'Ŵ', 'ŵ', 'Ŷ', 'ŷ',
'Ÿ', 'Ź', 'ź', 'Ż', 'ż', 'Ž', 'ž', 'ſ',
'ƀ', 'Ɓ', 'Ƃ', 'ƃ', 'Ƅ', 'ƅ', 'Ɔ', 'Ƈ',
'ƈ', 'Ɖ', 'Ɗ', 'Ƌ', 'ƌ', 'ƍ', 'Ǝ', 'Ə',
'Ɛ', 'Ƒ', 'ƒ', 'Ɠ', 'Ɣ', 'ƕ', 'Ɩ', 'Ɨ',
'Ƙ', 'ƙ', 'ƚ', 'ƛ', 'Ɯ', 'Ɲ', 'ƞ', 'Ɵ',
'Ơ', 'ơ', 'Ƣ', 'ƣ', 'Ƥ', 'ƥ', 'Ʀ', 'Ƨ',
'ƨ',

// '=', '*', '&', '^', '%', '$', '#', '@'
};

获取GB2312编码所有汉字

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
private static final Charset CHARSET = Charset.forName("GB2312");

/**
* 获取GB2312所有汉字
* “高位字节”的范围是0xB0-0xF7,“低位字节”的范围是0xA0-0xFE
*/
private static List<String> getGB2312() {
List<String> words = new ArrayList<>();
byte[] bytes = new byte[2];
for (int b1 = 176; b1 < 248; b1++) {
bytes[0] = (byte) b1;
for (int b2 = 161; b2 < 255; b2++) {
bytes[1] = (byte) b2;
words.add(new String(bytes, CHARSET));
}
}
return words;
}

/**
* 对于gb2312来讲,首字节码位从0×81至0×FE,尾字节码位分别是0×40至0×FE
*/
public static boolean isGB2312(String str) {
boolean isGB2312 = false;
char[] chars = str.toCharArray();
for (char c : chars) {
byte[] bytes = ("" + c).getBytes(CHARSET);
if (bytes.length == 2) {
int[] ints = new int[2];
ints[0] = bytes[0] & 0xff;
ints[1] = bytes[1] & 0xff;
if (ints[0] >= 0x81 && ints[0] <= 0xFE && ints[1] >= 0x40 && ints[1] <= 0xFE) {
isGB2312 = true;
break;
}
}
}
return isGB2312;
}

Alfred 使用iTerm

ADD:我在网上找到了一篇更完整的文章,文章中详细介绍了,这么配合使用alfred + iTerm2 + ssh config快速连接远程服务器:https://juejin.cn/post/6844903909916426248

Alfred执行命令时默认使用的是MaxOS系统自带的Terminal,而我平时使用的都是iTerm。在Github上找到了解决方案:https://github.com/vitorgalvao/custom-alfred-iterm-scripts

设置方式直接截图说明:

WechatIMG62.png

配置内容如下(这个内容是我从上面那个Github项目中Copy出来的,可以自行去项目中Copy最新的):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
-- For the latest version:
-- https://github.com/vitorgalvao/custom-alfred-iterm-scripts

-- Set this property to true to always open in a new window
property open_in_new_window : false

-- Set this property to false to reuse current tab
property open_in_new_tab : true

-- Handlers
on new_window()
tell application "iTerm" to create window with default profile
end new_window

on new_tab()
tell application "iTerm" to tell the first window to create tab with default profile
end new_tab

on call_forward()
tell application "iTerm" to activate
end call_forward

on is_running()
application "iTerm" is running
end is_running

on has_windows()
if not is_running() then return false

tell application "iTerm"
if windows is {} then return false
if tabs of current window is {} then return false
if sessions of current tab of current window is {} then return false

set session_text to contents of current session of current tab of current window
if words of session_text is {} then return false
end tell

true
end has_windows

on send_text(custom_text)
tell application "iTerm" to tell the first window to tell current session to write text custom_text
end send_text

-- Main
on alfred_script(query)
if has_windows() then
if open_in_new_window then
new_window()
else if open_in_new_tab then
new_tab()
else
-- Reuse current tab
end if
else
-- If iTerm is not running and we tell it to create a new window, we get two
-- One from opening the application, and the other from the command
if is_running() then
new_window()
else
call_forward()
end if
end if

-- Make sure a window exists before we continue, or the write may fail
repeat until has_windows()
delay 0.01
end repeat

send_text(query)
call_forward()
end alfred_script

这也备份一下修改之前的配置,防止丢失:

1
2
3
4
5
6
on alfred_script(q)
tell application "Terminal"
activate
do script q
end tell
end alfred_script

Ipv4地址段匹配

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class IpMatcher {
public static boolean matchIPV4(String ip, String cidr) {
String[] ips = ip.split("\\.");
if (ips.length != 4) {
return false;
}
int ipAddr = (Integer.parseInt(ips[0]) << 24)
| (Integer.parseInt(ips[1]) << 16)
| (Integer.parseInt(ips[2]) << 8) | Integer.parseInt(ips[3]);
int type = Integer.parseInt(cidr.replaceAll(".*/", ""));
int mask = 0xFFFFFFFF << (32 - type);
String cidrIp = cidr.replaceAll("/.*", "");
String[] cidrIps = cidrIp.split("\\.");
int cidrIpAddr = (Integer.parseInt(cidrIps[0]) << 24)
| (Integer.parseInt(cidrIps[1]) << 16)
| (Integer.parseInt(cidrIps[2]) << 8)
| Integer.parseInt(cidrIps[3]);
return (ipAddr & mask) == (cidrIpAddr & mask);
}

public static void main(String[] args) {
System.out.println(matchIPV4("172.16.0.1", "172.16.0.0/20"));
}
}

使用certbot申请https证书

我的博客图片放在阿里云的oss,oss绑定域名的https证书过期很久了,之前一直懒得弄,周末抽时间弄一下,证书是使用certbot申请的,步骤如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
➜  ~ sudo certbot certonly --manual --preferred-challenges=dns-01 --server=https://acme-v02.api.letsencrypt.org/directory

Saving debug log to /var/log/letsencrypt/letsencrypt.log
Please enter the domain name(s) you would like on your certificate (comma and/or
space separated) (Enter 'c' to cancel): file.mspring.org
Requesting a certificate for file.mspring.org

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Please deploy a DNS TXT record under the name:

_acme-challenge.file.mspring.org.

with the following value:

eyCcqOYGl7KWxr-mGpp7Y77j6iEV-rC0dXyq8-LnL3k

Before continuing, verify the TXT record has been deployed. Depending on the DNS
provider, this may take some time, from a few seconds to multiple minutes. You can
check if it has finished deploying with aid of online tools, such as the Google
Admin Toolbox: https://toolbox.googleapps.com/apps/dig/#TXT/_acme-challenge.file.mspring.org.
Look for one or more bolded line(s) below the line ';ANSWER'. It should show the
value(s) you've just added.

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Press Enter to Continue

Successfully received certificate.
Certificate is saved at: /etc/letsencrypt/live/file.mspring.org-0001/fullchain.pem
Key is saved at: /etc/letsencrypt/live/file.mspring.org-0001/privkey.pem
This certificate expires on 2022-06-24.
These files will be updated when the certificate renews.

NEXT STEPS:
- This certificate will not be renewed automatically. Autorenewal of --manual certificates requires the use of an authentication hook script (--manual-auth-hook) but one was not provided. To renew this certificate, repeat this same certbot command before the certificate's expiry date.

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
If you like Certbot, please consider supporting our work by:
* Donating to ISRG / Let's Encrypt: https://letsencrypt.org/donate
* Donating to EFF: https://eff.org/donate-le
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

Golang文件流下载,边下载边计算MD5

当时是为了测试文件网卡问题,发现同样的文件,在一个很烂的网卡中不断的下载,计算的文件MD5会不一样的问题。代码记录下,留作参考。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
package main

import (
"crypto/md5"
"encoding/hex"
"flag"
"fmt"
"io"
"net/http"
"os"
"strconv"
"time"
)

var (
url = "被下载文件链接"
data = make(map[string]int)
logFile = flag.String("logFile", os.Getenv("HOME")+"/"+"network-card-test.log", "日志文件路径") // /Users/xxx/Downloads/fuck.log
)

func main() {
flag.Parse()
for {
handle()
}
}

func handle() {
md5Str, err := download(url)
if err != nil {
log(err.Error())
return
}

defer func() {
count := data[md5Str]
data[md5Str] = count + 1
}()

count, found := data[md5Str]
if !found && len(data) > 0 {
log("文件MD5异常:" + md5Str + ", count=" + strconv.Itoa(count))
} else {
log("文件MD5:" + md5Str + ", count=" + strconv.Itoa(count))
}
}

func download(url string) (string, error) {
var (
err error
resp *http.Response
contentLen int64
downloadLen int
)
if resp, err = http.Get(url); err != nil {
return "", err
}

if contentLen, err = strconv.ParseInt(resp.Header.Get("Content-Length"), 10, 64); err != nil {
return "", err
}

defer func() {
_ = resp.Body.Close()
fmt.Print("\n")
}()

h := md5.New()
for {
buf := make([]byte, 1024)
c, e := resp.Body.Read(buf)
if e != nil && e != io.EOF {
return "", e
}
downloadLen += c

printProgress(downloadLen, contentLen)

h.Write(buf[0:c])

if e != nil && e == io.EOF {
break
}
}
return hex.EncodeToString(h.Sum(nil)), nil
}

func printProgress(downloadLen int, contentLen int64) {
fmt.Printf("\r")
fmt.Printf("Downloading... %d of %d", downloadLen, contentLen)
}

func log(content string) {
content = time.Now().Format("2006-01-02 15:04:05") + ": " + content

fmt.Println(content)

file, err := os.OpenFile(*logFile, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0644)
if err != nil {
fmt.Println(err.Error())
return
}
defer func() {
_ = file.Close()
}()

_, err = file.WriteString(content + "\n")
if err != nil {
fmt.Println(err.Error())
}
}