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

复习

1.黏包现象

粘包现象的成因:  

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

解决

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

那么这2种方案,如何选择呢?
如果发送数据之前,发送的定制协议超过1个变量时,就应该使用字典
否则,就需要发送多次,比如:文件名的长度,文件名,文件大小的长度,文件的内容...

发送的次数越多,在网络上,就比较浪费时间。
如果将上面的5个变量,放到一个字典里面,只需要发送一次,就可以了。

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

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

大文件传输:
server.py


import?os
import?json
import?socket
import?struct
filepath?=?r'E:\BaiduYunDownload\[电影天堂www.dy2018.com]移动迷宫3:死亡解药BD国英双语中英双字.mp4'
?
sk?=?socket.socket()
sk.bind(('127.0.0.1',9000))
sk.listen()
?
conn,addr?=?sk.accept()
filename?=?os.path.basename(filepath)
filesize?=?os.path.getsize(filepath)
dic?=?{'filename':filename,'filesize':filesize}
str_dic?=?json.dumps(dic).encode('utf-8')
len_dic?=?len(str_dic)
length?=?struct.pack('i',len_dic)
conn.send(length)???# dic的长度
conn.send(str_dic)??# dic
with?open(filepath,'rb') as f:??# 文件
????while?filesize:
????????content?=?f.read(4096)
????????conn.send(content)
????????filesize?-=?len(content)
????????'''
????????这里不能减等4096,因为文件,最后可能只有3字节。
????????要根据读取的长度len(content),来计算才是合理的。
????????'''
conn.close()
sk.close()

client.py


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

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

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

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


import?json
import?struct
import?socket
import?sys
import?time
?
def?processBar(num, total):??# 进度条
????rate?=?num?/?total
????rate_num?=?int(rate?*?100)
????if?rate_num?==?100:
????????r?=?'\r%s>%d%%\n'?%?('='?*?rate_num, rate_num,)
????else:
????????r?=?'\r%s>%d%%'?%?('='?*?rate_num, rate_num,)
????sys.stdout.write(r)
????sys.stdout.flush
?
start_time?=?time.time()??# 开始时间
?
sk?=?socket.socket()
sk.connect(('127.0.0.1',9000))
?
dic_len?=?sk.recv(4)
dic_len?=?struct.unpack('i',dic_len)[0]
dic?=?sk.recv(dic_len)
str_dic?=?dic.decode('utf-8')
dic?=?json.loads(str_dic)
with?open(dic['filename'],'wb') as f:??# 使用wb更严谨一些,虽然可以使用ab
????content_size?=?0
????while?True:
????????content?=?sk.recv(4096)<br>??????? f.write(content)?# 写入文件
????????content_size?+=?len(content)??# 接收大小
????????processBar(content_size,dic['filesize'])??# 执行进度条函数
????????if?content_size?==?dic['filesize']:break??# 当接收的总大小等于文件大小时,终止循环
?????????????
sk.close()??# 关闭连接
?
end_time?=?time.time()??# 结束时间
print('本次下载花费了{}秒'.format(end_time?-?start_time))

执行效果如下:

上面效果展示了100个等号,太长了,那么要缩减到1/3呢?
修改进度条函数


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

再次执行:

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


def?processBar(num, total):??# 进度条
????rate?=?num?/?total
????rate_num?=?int(rate?*?100)
????pretty?=?'?'
????if?rate_num?==?100:
????????r?=?'\r\033[32m{}\033[0m{}%\n'.format(pretty?*?int(rate_num?/?5), rate_num,)
????else:
????????r?=?'\r\033[32m{}\033[0m{}%'.format(pretty?*?int(rate_num?/?5), rate_num,)
????sys.stdout.write(r)
????sys.stdout.flush

效果如下:

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


import?random
?
?
class?Prompt(object):??# 提示信息显示
????colour_dic?=?{
????????'red':?31,
????????'green':?32,
????????'yellow':?33,
????????'blue':?34,
????????'purple_red':?35,
????????'bluish_blue':?36,
????????'white':?37,
????}
?
????def?__init__(self):
????????pass
?
[email protected]
????def?display(msg, colour='white'):
????????choice?=?Prompt.colour_dic.get(colour)
????????# print(choice)
????????if?choice:
????????????info?=?"\033[1;{};1m{}\033[1;0m".format(choice, msg)
????????????return?info
????????else:
????????????return?False
?
????def?random_color(msg):??# 随机换色
????????colour_list?=?[]
????????for?i?in?Prompt.colour_dic:
????????????colour_list.append(i)
?
????????length?=?len(colour_list)?-?1??# 最大索引值
????????index?=?random.randint(0, length)??# 随机数
?
????????ret?=?Prompt.display(msg, colour_list[index])??# 随机颜色
????????return?ret

修改client.py


from?Prompt?import?Prompt
?
def?processBar(num, total):??# 进度条
????rate?=?num?/?total
????rate_num?=?int(rate?*?100)
????pretty?=?Prompt.random_color('?')??# 随机换色
????if?rate_num?==?100:
????????r?=?'\r{}{}%\n'.format(pretty?*?int(rate_num?/?5), rate_num,)
????else:
????????r?=?'\r{}{}%'.format(pretty?*?int(rate_num?/?5), rate_num,)
????sys.stdout.write(r)
????sys.stdout.flush

执行效果如下:

1.增加MD5校验

server.py


import?os
import?json
import?socket
import?struct
import?hashlib
?
sk?=?socket.socket()
sk.bind(('127.0.0.1',?9000))
sk.listen()
?
conn, addr?=?sk.accept()
filename?=r?'[电影天堂www.dy2018.com]移动迷宫3:死亡解药BD国英双语中英双字.mp4'??# 文件名
absolute_path?=?os.path.join(r'E:\BaiduYunDownload',filename)??# 文件绝对路径
buffer_size?=?1024*1024??# 缓冲大小,这里表示1MB
?
md5obj?=?hashlib.md5()
with?open(absolute_path,?'rb') as f:
????while?True:
????????content?=?f.read(buffer_size)??# 每次读取指定字节
????????if?content:
????????????md5obj.update(content)
????????else:
????????????break??# 当内容为空时,终止循环
?
md5?=?md5obj.hexdigest()
print(md5)??# 打印md5值
?
dic?=?{'filename':filename,
???????'filename_md5':str(md5),'buffer_size':buffer_size,
???????'filesize':os.path.getsize(absolute_path)}
str_dic?=?json.dumps(dic).encode('utf-8')
len_dic?=?len(str_dic)
length?=?struct.pack('i', len_dic)
conn.send(length)??# dic的长度
conn.send(str_dic)??# dic
with?open(absolute_path,?'rb') as f:??# 文件
????while?dic['filesize']:
????????content?=?f.read(dic['buffer_size'])
????????conn.send(content)
????????dic['filesize']?-=?len(content)
????????'''
????????这里不能减等4096,因为文件,最后可能只有3字节。
????????要根据读取的长度len(content),来计算才是合理的。
????????'''
conn.close()

client.py


import?json
import?struct
import?socket
import?sys
import?time
import?hashlib
import?os
from?Prompt?import?Prompt
?
def?processBar(num, total):??# 进度条
????rate?=?num?/?total
????rate_num?=?int(rate?*?100)
????pretty?=?Prompt.random_color('?')
????if?rate_num?==?100:
????????r?=?'\r{}{}%\n'.format(pretty?*?int(rate_num?/?5), rate_num,)
????else:
????????r?=?'\r{}{}%'.format(pretty?*?int(rate_num?/?5), rate_num,)
????sys.stdout.write(r)
????sys.stdout.flush
?
start_time?=?time.time()??# 开始时间
?
sk?=?socket.socket()
sk.connect(('127.0.0.1',9000))
?
dic_len?=?sk.recv(4)
dic_len?=?struct.unpack('i',dic_len)[0]
dic?=?sk.recv(dic_len)
str_dic?=?dic.decode('utf-8')
dic?=?json.loads(str_dic)
?
md5?=?hashlib.md5()
with?open(dic['filename'],'wb') as f:??# 使用wb更严谨一些,虽然可以使用ab
????content_size?=?0
????while?True:
????????content?=?sk.recv(dic['buffer_size'])??# 接收指定大小
????????f.write(content)??# 写入文件
????????content_size?+=?len(content)??# 接收大小
????????md5.update(content)??# 摘要
?
????????processBar(content_size,dic['filesize'])??# 执行进度条函数
????????if?content_size?==?dic['filesize']:break??# 当接收的总大小等于文件大小时,终止循环
?
????md5?=?md5.hexdigest()
????print(md5)??# 打印md5值
????if?dic['filename_md5']?==?str(md5):
????????print(Prompt.display('md5校验正确--下载成功','green'))
????else:
????????print(Prompt.display('文件验证失败',?'red'))
????????os.remove(dic['filename'])??# 删除文件
?
sk.close()??# 关闭连接
?
end_time?=?time.time()??# 结束时间
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,失败则抛出异常。

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

#sock.sendall('Hello world\n')

#buffer = 'Hello world\n'
#while buffer:
#    bytes = sock.send(buffer)
#    buffer = buffer[bytes:]

验证客户端链接的合法性

使用hashlib.md5 加密

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


import?os
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


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

client.py


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

先执行server.py,再执行client.py
client输出:-----
server输出:是合法的客户端?

如果100客户端,来连接呢?秘钥都是通用的。
一般情况下,用在哪些场景呢?
比如公司级别,比如1台机器,向100台服务器获取数据

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

1.使用hmac加密

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

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

执行输出:
27111d37764a2fe5bc79d297e7b54c35
客户端也使用hmac,验证一下,就可以了。

改造server和client

server.py


import?os
import?socket
import?hmac
?
secret_key?=?'老衲洗头用飘柔'.encode('utf-8')
sk?=?socket.socket()
sk.bind(('127.0.0.1',9000))
sk.listen()
while?True:
????try:
????????conn,addr?=?sk.accept()
????????random_bytes?=?os.urandom(32)
????????conn.send(random_bytes)
????????obj?=?hmac.new(key=secret_key,msg=random_bytes)
????????ret?=?obj.hexdigest()
????????msg?=?conn.recv(1024).decode('utf-8')
????????if?msg?==?ret:print('是合法的客户端')
????????else:conn.close()
????finally:
????????sk.close()
????????break

client.py


import?socket
import?hmac
?
secret_key?=?'老衲洗头用飘柔'.encode('utf-8')
sk?=?socket.socket()
sk.connect(('127.0.0.1',?9000))
?
urandom?=?sk.recv(32)
hmac_obj?=?hmac.new(key=secret_key, msg=urandom)
sk.send(hmac_obj.hexdigest().encode('utf-8'))
print('-----')
sk.close()

socketserver

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

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

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

使用ThreadingTCPServer:
创建一个继承自 SocketServer.BaseRequestHandler 的类,必须继承
类中必须定义一个名称为 handle 的方法,必须重写

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


def?handle(self):
????pass

需要自己去实现
server.py


import?socketserver
class?MyServer(socketserver.BaseRequestHandler):
????def?handle(self):
????????print(self.request)
?
server?=?socketserver.ThreadingTCPServer(('127.0.0.1',9000),MyServer)
server.serve_forever()

client.py


import?socket
sk?=?socket.socket()
sk.connect(('127.0.0.1',9000))
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


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

client.py


import?socket
sk?=?socket.socket()
sk.connect(('127.0.0.1',9000))
print(sk.recv(1024))
inp?=?input('>>>').encode('utf-8')
sk.send(inp)
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连续发送:


import?socket
sk?=?socket.socket()
sk.connect(('127.0.0.1',9000))
while?True:
????print(sk.recv(1024))
????#inp = input('>>>').encode('utf-8')
????sk.send(b'hahaha')
sk.close()

server连续接收:


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

执行效果如下:

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


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

完整代码如下:


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

明日默写
```python
import socketserver
class MyServer(socketserver.BaseRequestHandler):
def handle(self):
self.request.send(b‘hello‘)
msg = self.request.recv(1024)
print(msg)

if name == ‘main‘:
socketserver.TCPServer.allow_reuse_address = True
server = socketserver.ThreadingTCPServer((‘127.0.0.1‘,9000),MyServer)
server.serve_forever()
```

原文地址:https://www.cnblogs.com/shy-kevin/p/11369368.html

时间: 2024-11-13 08:19:23

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

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

Asp.Net基于swfupload上传大文件带进度条百分比显示,漂亮大气上档次,大文件无压力,先看效果 一.上传效果图 1.上传前界面:图片不喜欢可以自己换 2.上传中界面:百分比显示 3.上传后返回文件地址,我测试呢所以乱写的 二.核心代码 upload.htm代码 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml

oss上传文件进度条展示

用户上传文件至oss的时候需要增加一个进度条展示,查看了官方文档及网上几篇博客后整理一下相关思路,在此记录一下自己的成长. 在此以上传视频为例,自定义监听监听文件上传进度,通过将字节数和总字节数之间比例写入session中返回给前端进行进度展示. private static String endpoint = "http://oss-cn-beijing.aliyuncs.com";     private static String accessKeyId = "<

android 文件下载及进度条显示

android 文件下载 进度条: ProgressDialog progress = new ProgressDialog(context); progress.setTitle(“文件下载”); progress.setMessage("loading..."); progress.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL); progress.setIndeterminate(false);//设置为fase等待进度更新,设

【Android】多文件下载进度条

实现多文件下载的进度条效果,实时展示下载进度. 下载地址:http://www.devstore.cn/code/info/329.html

基于Jquery的音乐播放器进度条插件

自己基于豆瓣FM的ui仿写qq音乐时,基于Jquery手写的进度条插件,效果图如下: 主要特色: ① 可自适应挂载元素的宽度,也可以自己设置进度条宽度: ② 支持部分默认参数修改(具体见使用说明): ③ 允许最大时间为23:59:59,高于此值将默认修改为此值: ④ 可以自己控制进度条动画的开关及重置: ⑤ 可以获取进度条当前时间和宽度,与H5的audio标签协调使用. 使用说明: /* * 功能描述:播放器进度条 * 参数: * option:挂载父元素 * inTime:进度条时间长度 *

Android子线程进度条不显示的问题

不显示进度条的代码逻辑: 首先,新建了一个类,此类有一个内部类,内部类继承Therad 类中有一个普通方法,普通方法执行的时候调用此方法中的内部类. 这时候,展示进度条. 进度条展示在普通方法中,那么实际展示的时间是在内部类执行完成后才展示,这样当然是不行的·· 进度条在子线程中展示则报错,百度的方法试过都不行.怎么办呢. 解决方案: 在类中建立一个普通的方法, 方法里直接new出子线程然后start 子线程中通过handler消息机制将耗时操作的数据结果返回给handler handler接收

HTML5-video(播放暂停视频;打开关闭声音;进度条)

<!DOCTYPE html> <html> <head> <title>HTML5-video(播放暂停视频:打开关闭声音:进度条)</title> <meta charset="utf-8"/> </head> <body> <video id="video1" controls="controls" width="400px&qu

使用html5实现文件上传进度条

html <div id="file"> <input type="file" name="ver" id="ver"/> <font color="red">*(apk,ipa目前为自动重命名)</font><br /> <div class="progress"><span class="bgpr

Android 常用效果(各种进度条,酷炫loading动画,火箭升空,撒花以及趋势图)

最近时间比较充裕一些,总结了下几个项目用到的ui效果,在这边共享给大家,也给自己做个记录(后面会有demo贴出). 主要是以下几种ui效果: 进度条多种展示 开源loading动画 火箭升空 撒花效果(仿微信) 气温趋势图 一.进度条 这边主要是有四种进度条展示,具体效果请往下看: 二.开源loading动画 这边有两种动画,来自于网上的开源demo,第一种是市场上app的比较常见动画,第二种是新版58的加载动画,具体效果请往下看: 三.火箭升空 这是在公司的一键清理项目中用到的一个动画,主要是