使用链接来直接存储小文件内容

# 杂项技术

背景

我们有的时候只是涉及到一些小文件,比如一些配置文件,一些小的图片等等,这些文件的内容不是很大,但是我们又不想直接把这些文件放到项目中,这个时候我们可以使用链接来直接存储这些小文件的内容。

而url支持使用 data 协议来使用base64格式直接存储小文件的内容,这样我们就可以直接使用链接来存储小文件的内容。

使用

我们可以使用如下的方式来存储小文件的内容:

1
<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAACXBIWXMAAAsSAAALEgHS3X78AAAA/0lEQVQ4jZXTsUoDQRDG8d8Q9" alt="Red dot" />

这里的 data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAACXBIWXMAAAsSAAALEgHS3X78AAAA/0lEQVQ4jZXTsUoDQRDG8d8Q9 就是一个base64格式的图片内容,我们可以直接使用这个链接来展示图片。

然后我写了一个简单的脚本来生成base64格式的图片内容:

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
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
#include <fstream>
#include <vector>
#include <string>
#include <iostream>

const std::string base64_chars =
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"
"0123456789+/";

#include <chrono>

std::string base64_encode(const std::vector<unsigned char>& buffer) {
std::string encoded;
int i = 0;
int j = 0;
unsigned char char_array_3[3];
unsigned char char_array_4[4];

int totalBytes = buffer.size();
int progressInterval = totalBytes / 10; // Update progress every 10% of the total bytes

for (const auto& byte : buffer) {
char_array_3[i++] = byte;
if (i == 3) {
char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
char_array_4[3] = char_array_3[2] & 0x3f;

for (i = 0; (i < 4); i++)
encoded += base64_chars[char_array_4[i]];
i = 0;

// Update progress
if (encoded.size() % progressInterval == 0) {
int progress = (encoded.size() * 100) / totalBytes;
std::cout << "Progress: " << progress << "%" << std::endl;
}
}
}

if (i) {
for (j = i; j < 3; j++)
char_array_3[j] = '\0';

char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);

for (j = 0; (j < i + 1); j++)
encoded += base64_chars[char_array_4[j]];

while ((i++ < 3))
encoded += '=';
}

return encoded;
}

void fileToBase64(const std::string& path) {
std::ifstream file(path, std::ios::binary);
std::vector<unsigned char> buffer(std::istreambuf_iterator<char>(file), {});

std::string base64 = base64_encode(buffer);
std::string dataUrl = "data:application/x-rar-compressed;base64," + base64;
//输出到 “downloadLink_”+文件名.md文件

//1.获取不带路径的文件名
std::string::size_type iPos = path.find_last_of('\\') + 1;
std::string fileName = path.substr(iPos, path.length() - iPos);
std::cout << fileName << std::endl;

//2.获取不带后缀的文件名
std::string fileNameWithoutExtension = fileName.substr(0, fileName.rfind("."));
std::cout << fileNameWithoutExtension << std::endl;

//3.获取后缀名
std::string suffix_str = fileName.substr(fileName.find_last_of('.') + 1);
std::cout << suffix_str << std::endl;

std::string outputFileName = "downloadLink_" + fileNameWithoutExtension + ".md";
std::ofstream output(outputFileName);
//输出到md文件
//为md文件添加属性
//---
// title: downloadLink_下载文件名称
// date: 2024-05-02 17:36
// mathjax: false
// tags:
// - Download
// ---
output << "---" << std::endl;
output << "title: " << fileName << std::endl;
output << "date: 2024-03-01 17:36" << std::endl;
output << "mathjax: false" << std::endl;
output << "tags:" << std::endl;
output << " - Download" << std::endl;
output << "---" << std::endl;
//输出下载链接,文件名为原文件名
//以按钮的形式显示下载链接
output << "<a href=\"" << dataUrl << "\" download=\""<< fileName <<"\">点击下载</a>";
output.close();

//再在文件夹“DownloadLink”里添加一个只有属性但是不包含下载链接的md文件
std::string outputFileName2 = "downloadLink_" + fileNameWithoutExtension + ".md";
std::ofstream output2("DownloadLink\\" + outputFileName2);
//输出到md文件
//为md文件添加属性
//---
// title: downloadLink_下载文件名称
// date: 2024-05-02 17:36
// mathjax: false
// tags:
// - Download
// ---
output2 << "---" << std::endl;
output2 << "title: " << fileName << std::endl;
output2 << "mathjax: false" << std::endl;
output2 << "date: 2024-03-01 17:36" << std::endl;
output2 << "tags:" << std::endl;
output2 << " - Download" << std::endl;
output2 << "---" << std::endl;
output2.close();

std::cout << "Output file: " << outputFileName << std::endl;
}

//接收文件位置参数
int main(int argc, char* argv[]) {
//检查参数
if (argc < 2) {
std::cout << "Usage: " << argv[0] << " file1 [file2] [file3] ..." << std::endl;
return 1;
}
//支持多个文件
for (int i = 1; i < argc; i++){
//显示进度条
std::cout << "Processing " << argv[i] << "..." << std::endl;
fileToBase64(argv[i]);
}

//结束后暂停
std::cout << "Press any key to exit..." << std::endl;
std::cin.get();
return 0;
}

这个脚本可以将指定的文件转换为base64格式的内容,并且生成一个md文件,这个md文件包含了下载链接,我们可以直接使用这个md文件来展示下载链接。

下面就是一个使用这个脚本生成的md文件的例子:

1
2
3
4
5
6
7
8
---
title: test.rar
date: 2024-03-01 17:36
mathjax: false
tags:
- Download
---
<a href="data:application/x-rar-compressed;base64,UEsDBAoAAAAAAKx4pVgvcG0lQAAAAEAAAAAMAAAAtePO0rXjztIudHh05ZOI5ZOI5ZOI77yM5piv5LiN5piv5oy65aW9546p55qEDQoNCuS9v+eUqOmTvuaOpeS8oOi+k+WOi+e8qeWMhVBLAQI/AAoAAAAAAKx4pVgvcG0lQAAAAEAAAAAMACQAAAAAAAAAIAAAAAAAAAC1487StePO0i50eHQKACAAAAAAAAEAGACtFN2Yup7aAVB935i6ntoBiknN7yme2gFQSwUGAAAAAAEAAQBeAAAAagAAAAAA" download="test.zip">点击下载</a>

点击下载

这样我们就可以直接使用这个md文件来展示下载链接了。