复习

1.黏包现象

粘包现象的成因:  

  • tcp协议的特点,面向流的,为了保证可靠传输,所以有很多优化的机制。 
  • 无边界 所有在连接建立的基础上传递的数据之间没有界限。
  • 收发消息很有可能不完全相等。
  • 缓存机制,导致没发过去的消息会在发送端缓存,没接收完的消息会在接收端缓存。

解决

  • 给应用层定制协议

    解决方案一:只发送一条信息
  • 先发送一个定长表示待发送数据长度的bytes,先接收一个固定长度  
  • 再发送要发送的数据,再按照长度接收数据。

    解决方案二 :发送的多条信息
  • 先发送一个定长表示待发送字典长度的bytes,先接收一个固定长度。
  • 再发送要发送字典(字典中存储的是文件信息),再按照长度接收字典。  
  • 再发送文件,再根据字典中的信息接收相应的内容。

那么这2种方案,如何选择呢?

如果发送数据之前,发送的定制协议超过1个变量时,就应该使用字典

否则,就需要发送多次,比如:文件名的长度,文件名,文件大小的长度,文件的内容...

发送的次数越多,在网络上,就比较浪费时间。

如果将上面的5个变量,放到一个字典里面,只需要发送一次,就可以了。

如果只有1个变量,就使用第一种方案。

大文件下载以及进度条展示

大文件传输:

server.py

  1. import os
  2. import json
  3. import socket
  4. import struct
  5. filepath = r'E:\BaiduYunDownload\[电影天堂www.dy2018.com]移动迷宫3:死亡解药BD国英双语中英双字.mp4'
  6.  
  7. sk = socket.socket()
  8. sk.bind(('127.0.0.1',9000))
  9. sk.listen()
  10.  
  11. conn,addr = sk.accept()
  12. filename = os.path.basename(filepath)
  13. filesize = os.path.getsize(filepath)
  14. dic = {'filename':filename,'filesize':filesize}
  15. str_dic = json.dumps(dic).encode('utf-8')
  16. len_dic = len(str_dic)
  17. length = struct.pack('i',len_dic)
  18. conn.send(length)   # dic的长度
  19. conn.send(str_dic)  # dic
  20. with open(filepath,'rb') as f:  # 文件
  21.     while filesize:
  22.         content = f.read(4096)
  23.         conn.send(content)
  24.         filesize -= len(content)
  25.         '''
  26.         这里不能减等4096,因为文件,最后可能只有3字节。
  27.         要根据读取的长度len(content),来计算才是合理的。
  28.         '''
  29. conn.close()
  30. sk.close()

client.py

  1. import json
  2. import struct
  3. import socket
  4.  
  5. sk = socket.socket()
  6. sk.connect(('127.0.0.1',9000))
  7.  
  8. dic_len = sk.recv(4)
  9. dic_len = struct.unpack('i',dic_len)[0]
  10. dic = sk.recv(dic_len)
  11. str_dic = dic.decode('utf-8')
  12. dic = json.loads(str_dic)
  13. with open(dic['filename'],'wb') as f:  # 使用wb更严谨一些,虽然可以使用ab
  14.     while dic['filesize']:
  15.         content = sk.recv(4096)  #这边的4096,可以不用和server对应,改成1024,也可以
  16.         dic['filesize'] -= len(content)
  17.         f.write(content)
  18. sk.close()

先执行server.py,再执行client.py等待30多秒,当前目录就会出现一个视频文件,打开确认,是否可以播放。

客户体验太差了,用户不知道啥时候能接收完,程序到底有没有卡住?下载花了多长时间?都不知道下面来一个进阶版的,增加进度条和下载时间

主要是修改client.py,代码如下:

  1. import json
  2. import struct
  3. import socket
  4. import sys
  5. import time
  6.  
  7. def processBar(num, total):  # 进度条
  8.     rate = num / total
  9.     rate_num = int(rate * 100)
  10.     if rate_num == 100:
  11.         r = '\r%s>%d%%\n' % ('=' * rate_num, rate_num,)
  12.     else:
  13.         r = '\r%s>%d%%' % ('=' * rate_num, rate_num,)
  14.     sys.stdout.write(r)
  15.     sys.stdout.flush
  16.  
  17. start_time = time.time()  # 开始时间
  18.  
  19. sk = socket.socket()
  20. sk.connect(('127.0.0.1',9000))
  21.  
  22. dic_len = sk.recv(4)
  23. dic_len = struct.unpack('i',dic_len)[0]
  24. dic = sk.recv(dic_len)
  25. str_dic = dic.decode('utf-8')
  26. dic = json.loads(str_dic)
  27. with open(dic['filename'],'wb') as f:  # 使用wb更严谨一些,虽然可以使用ab
  28.     content_size = 0
  29.     while True:
  30.         content = sk.recv(4096)<br>        f.write(content) # 写入文件
  31.         content_size += len(content)  # 接收大小
  32.         processBar(content_size,dic['filesize'])  # 执行进度条函数
  33.         if content_size == dic['filesize']:break  # 当接收的总大小等于文件大小时,终止循环
  34.              
  35. sk.close()  # 关闭连接
  36.  
  37. end_time = time.time()  # 结束时间
  38. print('本次下载花费了{}秒'.format(end_time - start_time))

执行效果如下:

上面效果展示了100个等号,太长了,那么要缩减到1/3呢?

修改进度条函数

  1. def processBar(num, total):  # 进度条
  2.     rate = num / total
  3.     rate_num = int(rate * 100)
  4.     if rate_num == 100:
  5.         r = '\r%s>%d%%\n' % ('=' * int(rate_num / 3), rate_num,) # 控制等号输出数量,除以3,表示显示1/3
  6.     else:
  7.         r = '\r%s>%d%%' % ('=' * int(rate_num / 3), rate_num,)
  8.     sys.stdout.write(r)
  9.     sys.stdout.flush

再次执行:

再来一个高级版,显示绿色的飞机代码如下

代码如下:

  1. def processBar(num, total):  # 进度条
  2.     rate = num / total
  3.     rate_num = int(rate * 100)
  4.     pretty = '✈'
  5.     if rate_num == 100:
  6.         r = '\r\033[32m{}\033[0m{}%\n'.format(pretty * int(rate_num / 5), rate_num,)
  7.     else:
  8.         r = '\r\033[32m{}\033[0m{}%'.format(pretty * int(rate_num / 5), rate_num,)
  9.     sys.stdout.write(r)
  10.     sys.stdout.flush

效果如下:

再来一个每秒换色,导入一个随机换色类。

  1. import random
  2.  
  3.  
  4. class Prompt(object):  # 提示信息显示
  5.     colour_dic = {
  6.         'red': 31,
  7.         'green': 32,
  8.         'yellow': 33,
  9.         'blue': 34,
  10.         'purple_red': 35,
  11.         'bluish_blue': 36,
  12.         'white': 37,
  13.     }
  14.  
  15.     def __init__(self):
  16.         pass
  17.  
  18.     @staticmethod
  19.     def display(msg, colour='white'):
  20.         choice = Prompt.colour_dic.get(colour)
  21.         # print(choice)
  22.         if choice:
  23.             info = "\033[1;{};1m{}\033[1;0m".format(choice, msg)
  24.             return info
  25.         else:
  26.             return False
  27.  
  28.     def random_color(msg):  # 随机换色
  29.         colour_list = []
  30.         for i in Prompt.colour_dic:
  31.             colour_list.append(i)
  32.  
  33.         length = len(colour_list) - 1  # 最大索引值
  34.         index = random.randint(0, length)  # 随机数
  35.  
  36.         ret = Prompt.display(msg, colour_list[index])  # 随机颜色
  37.         return ret

修改client.py

  1. from Prompt import Prompt
  2.  
  3. def processBar(num, total):  # 进度条
  4.     rate = num / total
  5.     rate_num = int(rate * 100)
  6.     pretty = Prompt.random_color('✈')  # 随机换色
  7.     if rate_num == 100:
  8.         r = '\r{}{}%\n'.format(pretty * int(rate_num / 5), rate_num,)
  9.     else:
  10.         r = '\r{}{}%'.format(pretty * int(rate_num / 5), rate_num,)
  11.     sys.stdout.write(r)
  12.     sys.stdout.flush

执行效果如下:

1.增加MD5校验

server.py

  1. import os
  2. import json
  3. import socket
  4. import struct
  5. import hashlib
  6.  
  7. sk = socket.socket()
  8. sk.bind(('127.0.0.1', 9000))
  9. sk.listen()
  10.  
  11. conn, addr = sk.accept()
  12. filename ='[电影天堂www.dy2018.com]移动迷宫3:死亡解药BD国英双语中英双字.mp4'  # 文件名
  13. absolute_path = os.path.join(r'E:\BaiduYunDownload',filename)  # 文件绝对路径
  14. buffer_size = 1024*1024  # 缓冲大小,这里表示1MB
  15.  
  16. md5obj = hashlib.md5()
  17. with open(absolute_path, 'rb') as f:
  18.     while True:
  19.         content = f.read(buffer_size)  # 每次读取指定字节
  20.         if content:
  21.             md5obj.update(content)
  22.         else:
  23.             break  # 当内容为空时,终止循环
  24.  
  25. md5 = md5obj.hexdigest()
  26. print(md5)  # 打印md5值
  27.  
  28. dic = {'filename':filename,
  29.        'filename_md5':str(md5),'buffer_size':buffer_size,
  30.        'filesize':os.path.getsize(absolute_path)}
  31. str_dic = json.dumps(dic).encode('utf-8')
  32. len_dic = len(str_dic)
  33. length = struct.pack('i', len_dic)
  34. conn.send(length)  # dic的长度
  35. conn.send(str_dic)  # dic
  36. with open(absolute_path, 'rb') as f:  # 文件
  37.     while dic['filesize']:
  38.         content = f.read(dic['buffer_size'])
  39.         conn.send(content)
  40.         dic['filesize'] -= len(content)
  41.         '''
  42.         这里不能减等4096,因为文件,最后可能只有3字节。
  43.         要根据读取的长度len(content),来计算才是合理的。
  44.         '''
  45. conn.close()

client.py

  1. import json
  2. import struct
  3. import socket
  4. import sys
  5. import time
  6. import hashlib
  7. import os
  8. from Prompt import Prompt
  9.  
  10. def processBar(num, total):  # 进度条
  11.     rate = num / total
  12.     rate_num = int(rate * 100)
  13.     pretty = Prompt.random_color('✈')
  14.     if rate_num == 100:
  15.         r = '\r{}{}%\n'.format(pretty * int(rate_num / 5), rate_num,)
  16.     else:
  17.         r = '\r{}{}%'.format(pretty * int(rate_num / 5), rate_num,)
  18.     sys.stdout.write(r)
  19.     sys.stdout.flush
  20.  
  21. start_time = time.time()  # 开始时间
  22.  
  23. sk = socket.socket()
  24. sk.connect(('127.0.0.1',9000))
  25.  
  26. dic_len = sk.recv(4)
  27. dic_len = struct.unpack('i',dic_len)[0]
  28. dic = sk.recv(dic_len)
  29. str_dic = dic.decode('utf-8')
  30. dic = json.loads(str_dic)
  31.  
  32. md5 = hashlib.md5()
  33. with open(dic['filename'],'wb') as f:  # 使用wb更严谨一些,虽然可以使用ab
  34.     content_size = 0
  35.     while True:
  36.         content = sk.recv(dic['buffer_size'])  # 接收指定大小
  37.         f.write(content)  # 写入文件
  38.         content_size += len(content)  # 接收大小
  39.         md5.update(content)  # 摘要
  40.  
  41.         processBar(content_size,dic['filesize'])  # 执行进度条函数
  42.         if content_size == dic['filesize']:break  # 当接收的总大小等于文件大小时,终止循环
  43.  
  44.     md5 = md5.hexdigest()
  45.     print(md5)  # 打印md5值
  46.     if dic['filename_md5'] == str(md5):
  47.         print(Prompt.display('md5校验正确--下载成功','green'))
  48.     else:
  49.         print(Prompt.display('文件验证失败', 'red'))
  50.         os.remove(dic['filename'])  # 删除文件
  51.  
  52. sk.close()  # 关闭连接
  53.  
  54. end_time = time.time()  # 结束时间
  55. print('本次下载花费了{}秒'.format(end_time - start_time))

执行输出:

socket的更多方法介绍

1.更多方法

服务端套接字函数

s.bind() 绑定(主机,端口号)到套接字

s.listen() 开始TCP监听

s.accept() 被动接受TCP客户的连接,(阻塞式)等待连接的到来

客户端套接字函数

s.connect() 主动初始化TCP服务器连接

s.connect_ex() connect()函数的扩展版本,出错时返回出错码,而不是抛出异常

公共用途的套接字函数

s.recv() 接收TCP数据

s.send() 发送TCP数据

s.sendall() 发送TCP数据

s.recvfrom() 接收UDP数据

s.sendto() 发送UDP数据

s.getpeername() 连接到当前套接字的远端的地址

s.getsockname() 当前套接字的地址

s.getsockopt() 返回指定套接字的参数

s.setsockopt() 设置指定套接字的参数

s.close() 关闭套接字

面向锁的套接字方法

s.setblocking() 设置套接字的阻塞与非阻塞模式

s.settimeout() 设置阻塞套接字操作的超时时间

s.gettimeout() 得到阻塞套接字操作的超时时间

面向文件的套接字的函数

s.fileno() 套接字的文件描述符

s.makefile() 创建一个与该套接字相关的文件

2.send和sendall方法

官方文档对socket模块下的socket.send()和socket.sendall()解释如下:

socket.send(string[, flags])

Send data to the socket. The socket must be connected to a remote socket. The optional flags argument has the same meaning as for recv() above. Returns the number of bytes sent. Applications are responsible for checking that all data has been sent; if only some of the data was transmitted, the application needs to attempt delivery of the remaining data.

send()的返回值是发送的字节数量,这个数量值可能小于要发送的string的字节数,也就是说可能无法发送string中所有的数据。如果有错误则会抛出异常。

socket.sendall(string[, flags])

Send data to the socket. The socket must be connected to a remote socket. The optional flags argument has the same meaning as for recv() above. Unlike send(), this method continues to send data from string until either all data has been sent or an error occurs. None is returned on success. On error, an exception is raised, and there is no way to determine how much data, if any, was successfully sent.

尝试发送string的所有数据,成功则返回None,失败则抛出异常。

故,下面两段代码是等价的:

  1. #sock.sendall('Hello world\n')
  2. #buffer = 'Hello world\n'
  3. #while buffer:
  4. # bytes = sock.send(buffer)
  5. # buffer = buffer[bytes:]

验证客户端链接的合法性

使用hashlib.md5 加密

为什么要随机字符串,是为了防止客户端的数据被窃取。生成随机的bytes类型数据,它是解不出来的。

  1. import os
  2. print(os.urandom(32))

执行输出:

b'PO\xca8\xc8\xf3\xa0\xb5,\xdd\xb8K \xa8D\x9cN"\x82\x03\x86g\x18e\xa7\x97\xa77\xb9\xa5VA'

server.py

  1. import os
  2. import socket
  3. import hashlib
  4.  
  5. secret_key = '老衲洗头用飘柔'  # 加密key
  6.  
  7. sk = socket.socket()
  8. sk.bind(('127.0.0.1',9000))
  9. sk.listen()
  10. while True:
  11.     try:
  12.         conn,addr = sk.accept()
  13.         random_bytes = os.urandom(32)  # 随即产生32个字节的字符串,返回bytes
  14.         conn.send(random_bytes)  # 发送随机加密key
  15.         md5 = hashlib.md5(secret_key.encode('utf-8'))  # 使用secret_key作为加密盐
  16.         md5.update(random_bytes)  #得到MD5消息摘要
  17.         ret = md5.hexdigest()  #以16进制返回消息摘要,它是一个32位长度的字符串
  18.         msg = conn.recv(1024).decode('utf-8')  # 接收的信息解码
  19.         if msg == ret:print('是合法的客户端')  # 如果接收的摘要和本机计算的摘要一致,就说明是合法的
  20.         else:conn.close()  # 关闭连接
  21.     finally:  # 无论如何,都执行下面的代码
  22.         sk.close()  # 关闭连接
  23.         break

client.py

  1. import socket
  2. import hashlib
  3. secret_key = '老衲洗头用飘柔'  # 加密key
  4. sk = socket.socket()
  5. sk.connect(('127.0.0.1',9000))
  6.  
  7. urandom = sk.recv(32)  # 接收32字节,也就是os.urandom的返回值
  8. md5_obj = hashlib.md5(secret_key.encode('utf-8'))  # 使用加密盐加密
  9. md5_obj.update(urandom)
  10. sk.send(md5_obj.hexdigest().encode('utf-8'))  # 发送md5摘要
  11. print('-----')
  12. sk.close()  # 关闭连接

先执行server.py,再执行client.py

client输出:-----

server输出:是合法的客户端

如果100客户端,来连接呢?秘钥都是通用的。

一般情况下,用在哪些场景呢?

比如公司级别,比如1台机器,向100台服务器获取数据

假如黑客渗透到内网,得知到服务器IP地址。就可以做端口扫描,一台计算机的端口范围是0~65535扫描6万多次,就能知道了。

1.使用hmac加密

hmac是专门来做客户端合法性的

  1. import hmac
  2. obj = hmac.new(key=b'secret_key',msg=b'100212002155')
  3. print(obj.hexdigest())

执行输出:

27111d37764a2fe5bc79d297e7b54c35

客户端也使用hmac,验证一下,就可以了。

改造server和client

server.py

  1. import os
  2. import socket
  3. import hmac
  4.  
  5. secret_key = '老衲洗头用飘柔'.encode('utf-8')
  6. sk = socket.socket()
  7. sk.bind(('127.0.0.1',9000))
  8. sk.listen()
  9. while True:
  10.     try:
  11.         conn,addr = sk.accept()
  12.         random_bytes = os.urandom(32)
  13.         conn.send(random_bytes)
  14.         obj = hmac.new(key=secret_key,msg=random_bytes)
  15.         ret = obj.hexdigest()
  16.         msg = conn.recv(1024).decode('utf-8')
  17.         if msg == ret:print('是合法的客户端')
  18.         else:conn.close()
  19.     finally:
  20.         sk.close()
  21.         break

client.py

  1. import socket
  2. import hmac
  3.  
  4. secret_key = '老衲洗头用飘柔'.encode('utf-8')
  5. sk = socket.socket()
  6. sk.connect(('127.0.0.1', 9000))
  7.  
  8. urandom = sk.recv(32)
  9. hmac_obj = hmac.new(key=secret_key, msg=urandom)
  10. sk.send(hmac_obj.hexdigest().encode('utf-8'))
  11. print('-----')
  12. sk.close()

socketserver

SocketServer内部使用 IO多路复用 以及 “多线程” 和 “多进程” ,从而实现并发处理多个客户端请求的Socket服务端。即:每个客户端请求连接到服务器时,Socket服务端都会在服务器是创建一个“线程”或者“进 程” 专门负责处理当前客户端的所有请求。

它能实现多个客户端,同时连接,它继承了socket

ThreadingTCPServer实现的Soket服务器内部会为每个client创建一个 “线程”,该线程用来和客户端进行交互。

使用ThreadingTCPServer:

创建一个继承自 SocketServer.BaseRequestHandler 的类,必须继承

类中必须定义一个名称为 handle 的方法,必须重写

看BaseRequestHandler 的源码,它的hendle方法,是空的

  1. def handle(self):
  2.     pass

需要自己去实现

server.py

  1. import socketserver
  2. class MyServer(socketserver.BaseRequestHandler):
  3.     def handle(self):
  4.         print(self.request)
  5.  
  6. server = socketserver.ThreadingTCPServer(('127.0.0.1',9000),MyServer)
  7. server.serve_forever()

client.py

  1. import socket
  2. sk = socket.socket()
  3. sk.connect(('127.0.0.1',9000))
  4. sk.close()

先执行server.py,再执行client.py

server输出

<socket.socket fd=540, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 9000), raddr=('127.0.0.1', 62243)>

每连接一个client.就会触发handle,输出request监听,等待连接,接收,全由hendle完成了。

server.py

  1. import socketserver
  2. class MyServer(socketserver.BaseRequestHandler):
  3.     def handle(self):
  4.         print(self.request)
  5.         self.request.send(b'hello')  # 跟所有的client打招呼
  6.         print(self.request.recv(1024))  # 接收客户端的信息
  7.  
  8. server = socketserver.ThreadingTCPServer(('127.0.0.1',9000),MyServer)
  9. server.serve_forever()

client.py

  1. import socket
  2. sk = socket.socket()
  3. sk.connect(('127.0.0.1',9000))
  4. print(sk.recv(1024))
  5. inp = input('>>>').encode('utf-8')
  6. sk.send(inp)
  7. sk.close()

先执行server.py,再执行client.py

client输出:b'hello'

>>>hiserver输出:<socket.socket fd=316, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 9000), raddr=('127.0.0.1', 49176)> b'hi'

开多个客户端,也可以执行

server能够和多个client通信

1.连续发送

client连续发送:

  1. import socket
  2. sk = socket.socket()
  3. sk.connect(('127.0.0.1',9000))
  4. while True:
  5.     print(sk.recv(1024))
  6.     #inp = input('>>>').encode('utf-8')
  7.     sk.send(b'hahaha')
  8. sk.close()

server连续接收:

  1. import socketserver
  2. class MyServer(socketserver.BaseRequestHandler):
  3.     def handle(self):
  4.         while True:
  5.             print(self.request)
  6.             self.request.send(b'hello')  # 跟所有的client打招呼
  7.             print(self.request.recv(1024))  # 接收客户端的信息
  8.  
  9. server = socketserver.ThreadingTCPServer(('127.0.0.1',9000),MyServer)
  10. server.serve_forever()

执行效果如下:

如果server端口重复,使用以下代码:

  1. # 设置allow_reuse_address允许服务器重用地址
  2.     socketserver.TCPServer.allow_reuse_address = True

完整代码如下:

  1. import socketserver
  2. class MyServer(socketserver.BaseRequestHandler):
  3.     def handle(self):
  4.         while True:
  5.             print(self.request)  # 这里不能使用input,否则卡住了
  6.             self.request.send(b'hello')  # 跟所有的client打招呼
  7.             print(self.request.recv(1024))  # 接收客户端的信息
  8. if __name__ == '__main__':
  9.     socketserver.TCPServer.allow_reuse_address = True
  10.     server = socketserver.ThreadingTCPServer(('127.0.0.1',9000),MyServer)
  11.     server.serve_forever()

明日默写

  1. import socketserver
  2. class MyServer(socketserver.BaseRequestHandler):
  3. def handle(self):
  4. self.request.send(b'hello')
  5. msg = self.request.recv(1024)
  6. print(msg)
  7. if __name__ == '__main__':
  8. socketserver.TCPServer.allow_reuse_address = True
  9. server = socketserver.ThreadingTCPServer(('127.0.0.1',9000),MyServer)
  10. server.serve_forever()

036_python的大文件下载以及进度条展示的更多相关文章

  1. python 全栈开发,Day36(作业讲解(大文件下载以及进度条展示),socket的更多方法介绍,验证客户端链接的合法性hmac,socketserver)

     先来回顾一下昨天的内容 黏包现象粘包现象的成因 : tcp协议的特点 面向流的 为了保证可靠传输 所以有很多优化的机制 无边界 所有在连接建立的基础上传递的数据之间没有界限 收发消息很有可能不完全相 ...

  2. python (大文件下载及进度条展示) 验证客户端链接的合法性,socketserver

    ##########总结########### 文件校验加进度条显示 ####server import os import json import socket import struct impo ...

  3. Asp.Net上传大文件带进度条swfupload

    Asp.Net基于swfupload上传大文件带进度条百分比显示,漂亮大气上档次,大文件无压力,先看效果 一.上传效果图 1.上传前界面:图片不喜欢可以自己换 2.上传中界面:百分比显示 3.上传后返 ...

  4. JS原生上传大文件显示进度条-php上传文件

    JS原生上传大文件显示进度条-php上传文件 在php.ini修改需要的大小: upload_max_filesize = 8M    post_max_size = 10M    memory_li ...

  5. PHP_APC扩展dll上传大文件及进度条实例

    1.弄好了APC之后,就是使用它了,下面是个例子,是一个进度条上传的例子,作为笔记记录下来 在这个例子之前,我们需要做如下的设置,如果我们需要上传的是大文件的话,请在您的php.ini文件中做如下的设 ...

  6. PHP 文件下载 显示进度条

    前台调用:js调用: function downloadfile(id,name,price,curcount_pricelimit){ Date.prototype.Format = functio ...

  7. 一个简单、易用的Python命令行(terminal)进度条库

    eprogress 是一个简单.易用的基于Python3的命令行(terminal)进度条库,可以自由选择使用单行显示.多行显示进度条或转圈加载方式,也可以混合使用. 示例 单行进度条 多行进度条 圆 ...

  8. ProgressBar 进度条开源项目总结

    在Android开发中,我们不免会遇到进度条展示的需求,以下是本人之前star的开源项目,供大家参考: 一.ArcProgressBar 开源项目地址:https://github.com/zenoT ...

  9. R语言学习笔记:使用tcltk包显示进度条

    一般在跑耗时较长的程序时,我们不知道程序到底有没有正常跑着,或者在爬虫的时候不知道爬到什么时候断了.因此可以添加进度条来显示当前进度,观察进度是否有进展.当进度条卡住的时候,可以判断程序断线,从而可以 ...

随机推荐

  1. AJ学IOS(20)UI之UIPickerView_点菜系统

    AJ分享,必须精品 先看效果图 ## UIPickerView控件 UIPickerView用处: 用来展示很多行(row) 很多列(component )的数据,多用于电子商务的点菜,城市选择等等. ...

  2. 手把手教Extjs-简单GridField示例讲解二

    使用的Extjs版本为4.2,示例是官方的版本,对里面的语法进行一句一句的学习研究.可以方便他人,又可以提升自己的理解.里面存在的问题,后期会一步一步改进.也欢迎各位指出. /* Extjs具有很庞大 ...

  3. api测试用例(编写思路)

    在API的自动化测试维度中,测试维度分为两个维度,一个是单独的对API的验证,客户端发送一个请求后,服务端得到客户端的请求并且响应回复给客户端: 另外一个维度是基于业务场景的测试,基于业务场景的也就是 ...

  4. Spring Boot将Mybatis返回结果转为驼峰的三种实现方式

    本文不再更新,可能存在内容过时的情况,实时更新请访问原地址:Spring Boot将Mybatis返回结果转为驼峰的三种实现方式: 我们通常获取Mybatis返回的数据结果时想要将字段以驼峰的形式返回 ...

  5. vue原生表格怎样实现动态列及表格数据下载

    最近项目经常用到带有合并效果以及动态列的表格,而翻阅iview和element-ui官网没有找到合适的(也有可能自己的水平有限,不会改写),所以只好自己用原生表格写了一个,具体效果如下: 这个表格右侧 ...

  6. windows下部署.netcore+docker系列二 (unbuntu 18.4 下 安装 docker)亲测!!!

    1.卸载sudo apt-get remove docker docker-engine docker.io containerd runc2.更新sudo apt-get update3.安装依赖包 ...

  7. 【FishFX】花式撩骚,打造TypeScript易用框架。

    · 栗子入手 假设有以下foo数组,数组中每个对象都拥有id,name两个属性,现在需要查找id > 0的对象数量. const foo: Array<{ id: number, name ...

  8. php算--------法

    <?php //冒泡排序:两两交换数值,最小的值在最左边,就如最轻的气泡在最上边.对整列数两两交换一次//最小的数在最左边,每次都能得一个在剩下的数中的最小 的数//“冒”出来的数组成一个有序区 ...

  9. JAVA企业级应用TOMCAT实战(一)

    一. Tomcat简介 Tomcat是Apache软件基金会(Apache Software Foundation)的Jakarta 项目中的一个核心项目,由Apache.Sun和其他一些公司及个人共 ...

  10. 接近8000字的Spring/SpringBoot常用注解总结!安排!

    0.前言 大家好,我是 Guide 哥!这是我的 221 篇优质原创文章.如需转载,请在文首注明地址,蟹蟹! 本文已经收录进我的 75K Star 的 Java 开源项目 JavaGuide:http ...