golang版本进度条演示:
[root@node1 build]# ipfs add main.cc
added QmZgunUwQgnQ4T5NmZYhTLECi1srQYXfSkELhaY5NoGkem main.cc
927 B / 927 B [======================================================================================================================================] 100.00%[root@node1 build]#
[root@node1 build]# ipfs add /root/git.tar.bz2
added QmfWdhVNERNKL2XJeRLfgmsXvYTH27m6bLVNXnpt7WPJwh git.tar.bz2
133.82 MiB / 133.82 MiB [============================================================================================================================] 100.00%
[root@node1 build]# ipfs add test
1.57 GiB / 2.00 GiB [===============================================================================================>-------------------------] 78.58% 00m06s
added QmTbKT35oQzKgR34LUHaPdtrFrphjxiQJLrz6QMyU4kQNC test
2.00 GiB / 2.00 GiB [================================================================================================================================] 100.00%[root@node1 build]#
实现进度条:
格式:
最左边开始的数是当前写入的大小/文件总大小,单位根据文件总大小不同分别为:B、MiB、GiB
紧接着,是一个以’[’、’=’、 ‘>’、’-’、’]'为显示字符的进度条,实时显示文件写入仓库的(状态条)百分比例。
再往右,是文件已写入的(数字格式)百分比。
特点:
- 字节为单位时,没有小数位
- MiB或GiB位单位时,精确到小数点后两位数值
- 当文件超过1G时,最右边不仅显示百分百,还显示预估的剩余时间,以01m06s这样的格式
- 进度条的容量不是100个’=’,而是加上其它字符,可以填满整个一行。
设计方法:
首先,可以通过ioctl获取终端的行和列,此处我们只取列即可。
最右边的字符宽度是可以确定的,整个列数也是可以确定的,最左边虽然是动态的(数值变化),但是可以在sprintf打印入Buffer时,获取到实际宽度。因此,左、中、右的字符宽度都可以计算出来。 字符个数也是可以确定的。那么剩下就是计算数值。
数值很好计算,文件总大小是固定的,写入多少文件也是可以读取到的,中间的进度条,按照百分比,乘以进度条满格时总的字符数量,即可打印出一个完整的进度条。
c++ 代码实现:
bool add_file(const char * filepath)
{
if (false == is_file_exist(filepath)) {
cout << "please source file exist!" << endl;
return false;
}
int64_t total_size = file_size(filepath);
if (-1 == total_size) {
return false;
}
size_t slices_cnt = total_size / block_unit;
size_t free = total_size % block_unit;
size_t all_slices_count = slices_cnt;
if (free) {
all_slices_count += 1;
}
//生成links文件名,json文件
string list_name = get_sha1_value(filepath);
//打开源文件
int fd = open(filepath, O_RDONLY);
if (-1 == fd) {
perror("open links file error");
}
// 进度条
const char *lable = "|/-\\";
int64_t w_bytes = 0;
char buffer[512] = {0};
char bar[512] = {0};
int64_t percent = 0;
fflush(stdout);
struct winsize win_size, kk;
auto get_win_size = [& win_size] (void) {
ioctl(STDIN_FILENO,TIOCGWINSZ,&win_size);
return true;
};
get_win_size();
//cout << " 写文件切片到ipfs对应仓库,生成links文件(objects链表,json格式)" << endl;
bool flag_run = true;
size_t object_cursor = 0, list_index = 0;
cout << "added " << list_name << " " << basename(filepath) << endl;
unsigned char * buf = new unsigned char[block_unit]();
while (flag_run) {
//...
while (flag_run) {
size_t ret = read_file(fd, buf, block_unit);
if (ret <= 0) {
flag_run = false;
//cout << "????????filepath=" << filepath << "size" << ret << endl;
break;
}
sha1 = get_sha1_value(buf, ret);
// ...
//写block到ipfs仓库
string object_path = lookup_object_directory(sha1) + "/" + sha1;
int fd_obj = open_file(object_path.c_str(), 666);
if (fd_obj == -1) {
perror("open block file failed!");
flag_run = false;
cout << "object_path : " << object_path << endl;
}
write_file(fd_obj, buf, ret);
close(fd_obj);
//cout << "write " << ret << "byte data to" << object_path << endl;
w_bytes += ret;
object_cursor++;
if (! (object_cursor % 0xff)) {
break;
}
}
//...
list_index++;
// 进度条控制
{
int64_t fenmu_zhengshu, fenmu_xiaoshu;
int64_t fenzi_zhengshu, fenzi_xiaoshu;
const char * p_unit;
percent = w_bytes * 100 / total_size;
percent %= 101;
memset(bar, '-', 100);
memset(bar, '=', percent);
//memset(bar + percent, '-', percent);
bar[100] = '\0';
if (total_size >= 1024 * 1024 *1024) {
p_unit = "GiB";
fenzi_zhengshu = w_bytes / 1024 / 1024 / 1024;
fenzi_xiaoshu = w_bytes % (1024 * 1024 * 1024) / (1024 * 1024) / 10;
fenmu_zhengshu = total_size / 1024 / 1024 / 1024;
fenmu_xiaoshu = total_size % (1024 * 1024 * 1024) / (1024 * 1024) / 10;
int ret = sprintf(buffer, " %ld.%02ld %s / %ld.%02ld %s ",
fenzi_zhengshu, fenzi_xiaoshu, p_unit,
fenmu_zhengshu, fenmu_xiaoshu, p_unit);
int per = win_size.ws_col - ret - 10;
bar[per] = '\0';
memset(bar, '-', per);
memset(bar, '=', percent * per / 100);
sprintf(buffer + ret, "[%-*s] %3ld%%[%c]\r",
per, bar, percent, lable[percent%4]);
}
else if (total_size >= 1024 * 1024) {
p_unit = "MiB";
fenzi_zhengshu = w_bytes / 1024 / 1024;
fenzi_xiaoshu = w_bytes % (1024 * 1024) / 1024 / 10;
fenzi_xiaoshu /= 1024;
fenmu_zhengshu = total_size / 1024 / 1024;
fenmu_xiaoshu = total_size % (1024 * 1024 ) / 1024 / 10;
fenmu_xiaoshu /= 1024;
int ret = sprintf(buffer, " %ld.%02ld %s / %ld.%02ld %s ",
fenzi_zhengshu, fenzi_xiaoshu, p_unit, fenmu_zhengshu, fenmu_xiaoshu, p_unit);
int per = win_size.ws_col - ret - 10;
bar[per] = '\0';
memset(bar, '-', per);
memset(bar, '=', percent * per / 100);
sprintf(buffer + ret, "[%-*s] %3ld%%[%c]\r",
per, bar, percent, lable[percent%4]);
}
else {
p_unit = " B";
fenzi_zhengshu = w_bytes;
fenmu_zhengshu = total_size;
int ret1 = sprintf(buffer, " %ld %s / %ld %s ",
fenzi_zhengshu, p_unit, fenmu_zhengshu, p_unit);
int per = win_size.ws_col - ret1 - 10;
bar[per] = '\0';
memset(bar, '-', per);
memset(bar, '=', percent * per / 100);
sprintf(buffer + ret1, "[%-*s] %3ld%%[%c]\r",
per, bar, percent, lable[percent%4]);
}
printf("%s", buffer);
fflush(stdout);
}
}
cout << endl;
fflush(stdout);
//cout << "w_bytes: " << w_bytes << endl;
// 写文件到仓库目录
//...
delete buf;
close(fd);
return true;
}
c++版本进度条效果:
[root@node1 build]# ./bin/ipfs --add main.cc
added d1978dd8e880118c7177b26e2dc381517b85abe0 main.cc
927 B / 927 B [====================================================================================================================================] 100%[|]
[root@node1 build]#
[root@node1 build]# ./bin/ipfs --add /root/git.tar.bz2
added bc41f0e189b5453a6ef1a6c64826fca0a3872835 git.tar.bz2
133.00 MiB / 133.00 MiB [============================================================================================================================] 100%[|]
[root@node1 build]# ./bin/ipfs --add test
added 91d50642dd930e9542c39d36f0516d45f4e1af0d test
2.00 GiB / 2.00 GiB [================================================================================================================================] 100%[|]
[root@node1 build]#
注意:
golang版本的哈希值是用sha256()生成的,其中Qm,Q代表是字符串类型为id,m代表id是使用base64编码。 既然做的不是与golang社区版本兼容的,即可不用插入Qm,看项目开发进度添加其它头即可,比如某知名p2p下载软件公司的存储项目,就是扩展了ipfs的编码格式。
ipfs白皮书定义的数据编码格式为:
multibase自描述基编码
multibase 代表的是一种编码格式, 方便把数据编码成不同的格式, 比如这里定义了2进制、8进制、10进制、16进制、也有我们熟悉的 base58btc 和 base64 编码。
支持的编码格式如下:
Multibase Table v1.0.0-RC (semver)
encoding codes name
identity 0x00 8-bit binary (encoder and decoder keeps data unmodified)
base1 1 unary tends to be 11111
base2 0 binary has 1 and 0
base8 7 highest char in octal
base10 9 highest char in decimal
base16 F, f highest char in hex
base32 B, b rfc4648 - no padding - highest letter
base32pad C, c rfc4648 - with padding
base32hex V, v rfc4648 - no padding - highest char
base32hexpad T, t rfc4648 - with padding
base32z h z-base-32 - used by Tahoe-LAFS - highest letter
base58flickr Z highest char
base58btc z highest char
base64 m rfc4648 - no padding
base64pad M rfc4648 - with padding - MIME encoding
base64url u rfc4648 - no padding
base64urlpad U rfc4648 - with padding