由于云盘空间有限,照片尺寸也是很大,所以写个Python程序压缩一下照片,腾出一些云盘空间

1、批量压缩照片

新建 photo_compress.py 代码如下

  1 # -*- coding: utf-8 -*-
2
3 """脚本功能说明:使用 tinypng api,一键批量压缩指定文件(夹)所有文件"""
4
5 import os
6 import sys
7 from concurrent.futures import ThreadPoolExecutor, ProcessPoolExecutor # 线程池,进程池
8 import json
9 import random
10 import requests
11 from you_get import common
12 from shutil import copyfile
13
14
15 def get_file_dir(file):
16 """获取文件目录通用函数"""
17 fullpath = os.path.abspath(os.path.realpath(file))
18 return os.path.dirname(fullpath)
19
20
21 def check_suffix(file_path):
22 """检查指定文件的后缀是否符合要求"""
23 file_path_lower = file_path.lower()
24 return (file_path_lower.endswith('.png')
25 or file_path_lower.endswith('.jpg')
26 or file_path_lower.endswith('.jpeg'))
27
28
29 def download_tinypng(input_file, url, output_file):
30 file_name = os.path.basename(input_file)
31 arr = file_name.split('.')
32 new_file_name = arr[len(arr) - 2] + '_compress'
33 new_output_file = os.path.join(os.path.dirname(output_file), arr[len(arr) - 2] + '_compress.' + arr[len(arr) - 1])
34 print(u'开始下载文件 :%s' % new_output_file)
35 # print(os.path.splitext(os.path.basename(output_file))[0])
36 sys.argv = ['you-get', '-o', os.path.dirname(
37 output_file), '-O', new_file_name, url]
38 common.main()
39 old_size = os.path.getsize(input_file)
40 new_size = os.path.getsize(new_output_file)
41 print(u'文件保存地址:%s' % new_output_file)
42 print(u'压缩后文件大小:%d KB' % (new_size / 1024))
43 print(u'压缩比: %d%%' % ((old_size - new_size) * 100 / old_size))
44
45
46 def compress_by_tinypng(input_file):
47 if not check_suffix(input_file):
48 print(u'只支持png\\jpg\\jepg格式文件:' + input_file)
49 return
50
51 file_name = os.path.basename(input_file)
52 arr = file_name.split('.')
53 new_file_name = arr[len(arr) - 2] + '_compress.' + arr[len(arr) - 1]
54 output_path = os.path.join(get_file_dir(input_file), 'compress_output')
55 output_file = os.path.join(output_path, new_file_name)
56 if not os.path.isdir(output_path):
57 os.makedirs(output_path)
58
59 if (os.path.exists(output_file)):
60 print("已存在,跳过压缩")
61 return
62
63 try:
64 old_size = os.path.getsize(input_file)
65 print(u'压缩前文件名:%s文件大小:%d KB' % (input_file, old_size / 1024))
66 if (old_size < 1024 * 1024):
67 print("已跳过压缩,并直接拷贝文件")
68 try:
69 copyfile(input_file, output_file)
70 except IOError as e:
71 print("Unable to copy file. %s" % e)
72 return
73 print("开始压缩")
74 shrink_image(input_file)
75 print(u'文件压缩成功:%s' % input_file)
76 # download_thread_pool.submit(download_tinypng, source, input_file, output_file)
77 except Exception as e:
78 print(u'报错了:%s' % e)
79
80
81 def check_path(input_path):
82 """如果输入的是文件则直接压缩,如果是文件夹则先遍历"""
83 if os.path.isfile(input_path):
84 compress_by_tinypng(input_path)
85 elif os.path.isdir(input_path):
86 dirlist = os.walk(input_path)
87 for root, dirs, files in dirlist:
88 if (not (root.endswith("\\compress_output") or root.endswith("/compress_output"))):
89 i = 0
90 for filename in files:
91 i = i + 1
92 process_pool.submit(compress_by_tinypng, os.path.join(
93 root, filename))
94 # compress_by_tinypng(os.path.join(root, filename))
95 else:
96 print(u'目标文件(夹)不存在,请确认后重试。')
97
98
99 def list_images(path):
100 images = None
101 try:
102 if path:
103 os.chdir(path)
104 full_path = os.getcwd()
105 files = os.listdir(full_path)
106 images = []
107 for file in files:
108 ext = os.path.splitext(file)[1].lower()
109 if ext in ('.jpg', '.jpeg', '.png'):
110 images.append(os.path.join(full_path, file))
111 except:
112 pass
113 return images
114
115
116 def shrink_image(file_path):
117 print(u'源文件地址:%s' % file_path)
118 result = shrink(file_path)
119 if result:
120 output_path = generate_output_path(file_path)
121 url = result['output']['url']
122 print(u'下载地址:%s' % url)
123 download_tinypng(file_path, url, output_path)
124 # download_thread_pool.submit(download_tinypng, file_path, url, output_path)
125 # response = requests.get(url)
126 # with open(output_path, 'wb') as file:
127 # file.write(response.content)
128 # print(u'文件保存地址:%s' % output_path)
129 # print('%s %d=>%d(%f)' % (
130 # result['input']['type'],
131 # result['input']['size'],
132 # result['output']['size'],
133 # result['output']['ratio']
134 # ))
135 else:
136 print('压缩失败')
137
138
139 def shrink(file_path):
140 url = 'https://tinypng.com/web/shrink'
141 headers = {
142 'Cache-Control': 'no-cache',
143 'Content-Type': 'application/x-www-form-urlencoded',
144 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.83 Safari/537.36 Edg/85.0.564.44',
145 'X-Forwarded-For': get_random_ip()
146 }
147 result = None
148 try:
149 file = open(file_path, 'rb')
150 response = requests.post(url, headers=headers, data=file)
151 result = json.loads(response.text)
152 except Exception as e:
153 print(u'报错了:%s' % e)
154 if file:
155 file.close()
156 if result and result['input'] and result['output']:
157 return result
158 else:
159 return None
160
161
162 def generate_output_path(file_path):
163 parent_path = os.path.abspath(os.path.dirname(file_path))
164 output_path = os.path.join(parent_path, 'compress_output')
165 if not os.path.isdir(output_path):
166 os.mkdir(output_path)
167 return os.path.join(output_path, os.path.basename(file_path))
168
169
170 def get_random_ip():
171 ip = []
172 for i in range(4):
173 ip.append(str(random.randint(0 if i in (2, 3) else 1, 254)))
174 return '.'.join(ip)
175
176
177 if __name__ == '__main__':
178 thread_pool = ThreadPoolExecutor(5) # 定义5个线程执行此任务
179 download_thread_pool = ThreadPoolExecutor(10) # 定义5个线程执行此任务
180 process_pool = ProcessPoolExecutor(8) # 定义5个进程
181 len_param = len(sys.argv)
182 if len_param != 2 and len_param != 3:
183 print('请使用: %s [filepath]' % os.path.basename(sys.argv[0]))
184 else:
185 check_path(sys.argv[1])
186 input("Press <enter> 请耐心等待\n")

执行python .\photo_compress.py F:\\test

生成compress_output文件夹,里面就是压缩的文件,但此时的照片没有,拍摄时的时间、位置的信息,所以下面要复制文件信息

若要压缩的文件不全,可以再执行一次压缩(会自动过滤已压缩的照片)

2、批量拷贝照片信息

使用pyexiv2进行文件信息拷贝

pip install pyexiv2 -i https://pypi.tuna.tsinghua.edu.cn/simple

新建 copy_fileinfo.py 代码如下

  1 # -*- coding: utf-8 -*-
2
3 """脚本功能说明:使用 pyexiv2 api,一键批量拷贝指定文件(夹)所有文件信息"""
4
5 import os
6 import sys
7 from concurrent.futures import ThreadPoolExecutor, ProcessPoolExecutor # 线程池,进程池
8 from pyexiv2 import Image
9
10
11 def get_file_dir(file):
12 """获取文件目录通用函数"""
13 fullpath = os.path.abspath(os.path.realpath(file))
14 return os.path.dirname(fullpath)
15
16
17 def check_suffix(file_path):
18 """检查指定文件的后缀是否符合要求"""
19 file_path_lower = file_path.lower()
20 return (file_path_lower.endswith('.png')
21 or file_path_lower.endswith('.jpg')
22 or file_path_lower.endswith('.jpeg'))
23
24
25 def copyinfo_by_pyexiv2(input_file):
26 file_name = os.path.basename(input_file)
27 arr = file_name.split('.')
28 new_file_name = arr[len(arr) - 2] + '_compress.' + arr[len(arr) - 1]
29 output_path = os.path.join(get_file_dir(input_file), 'compress_output')
30 output_file = os.path.join(output_path, new_file_name)
31 if not (check_suffix(input_file) or check_suffix(output_file)):
32 print(u'只支持png\\jpg\\jepg格式文件:' + input_file)
33 return
34 if not (os.path.exists(output_file)):
35 print(u'文件不存在:' + output_file)
36 return
37 old_size = os.path.getsize(input_file)
38 if (old_size < 1024 * 1024):
39 print(u"已跳过拷贝文件信息:", input_file)
40 return
41
42 # if not os.path.isdir(output_path):
43 # os.makedirs(output_path)
44 try:
45 i = Image(input_file) # 源图片路径
46 except Exception:
47 i = Image(input_file, "GB18030")
48
49 try:
50 _exif_info = i.read_exif()
51 except Exception:
52 _exif_info = i.read_exif("GB18030")
53
54 # print(_exif_info)
55 # _iptc_info = i.read_iptc()
56 # print(_iptc_info)
57 # _xmp_info = i.read_xmp()
58 # print(_xmp_info)
59 i.close()
60
61 try:
62 i2 = Image(output_file) # 拷贝信息图片路径
63 except Exception:
64 i2 = Image(output_file, "GB18030")
65
66 try:
67 _exif_info2 = i2.read_exif()
68 except Exception:
69 _exif_info2 = i2.read_exif("GB18030")
70
71 # 方向不拷贝,防止图片旋转
72 for item in _exif_info:
73 if("Exif.Image.Orientation" != item):
74 if (_exif_info2.get(item) != _exif_info.get(item)):
75 try:
76 i2.modify_exif({item: _exif_info[item]})
77 except Exception as e:
78 print(e)
79 try:
80 i2.modify_exif({item: _exif_info[item]}, "GB18030")
81 except Exception as e:
82 print(e)
83
84 i2.close()
85
86 print(u"拷贝信息完成:" + input_file)
87
88
89 def check_path(input_path):
90 """如果输入的是文件则直接压缩,如果是文件夹则先遍历"""
91 if os.path.isfile(input_path):
92 copyinfo_by_pyexiv2(input_path)
93 elif os.path.isdir(input_path):
94 dirlist = os.walk(input_path)
95 for root, dirs, files in dirlist:
96 if (not (root.endswith("\\compress_output") or root.endswith("/compress_output"))):
97 i = 0
98 for filename in files:
99 i = i + 1
100 process_pool.submit(copyinfo_by_pyexiv2, os.path.join(
101 root, filename))
102 else:
103 print(u'目标文件(夹)不存在,请确认后重试。')
104
105
106 if __name__ == '__main__':
107 # thread_pool = ThreadPoolExecutor(10) # 定义5个线程执行此任务
108 process_pool = ProcessPoolExecutor(8) # 定义5个进程
109 len_param = len(sys.argv)
110 if len_param != 2:
111 print('请使用: %s [filepath]' % os.path.basename(sys.argv[0]))
112 else:
113 check_path(sys.argv[1])
114 input("Press <enter> 请耐心等待\n")

执行python .\copy_fileinfo.py F:\\test

大功告成!图片压缩完毕,信息还没有丢失

python 无损压缩照片,支持批量压缩,支持保留照片信息的更多相关文章

  1. python监控文件实时批量压缩脚本

    # coding:utf-8 from shutil import make_archive import os import time # 指定需要监测的文件夹 image_path = './im ...

  2. 10 行 Python 代码,批量压缩图片 500 张,简直太强大了

    本文原创并首发于公众号[Python猫],未经授权,请勿转载. 原文地址:https://mp.weixin.qq.com/s/5hpFDgjCpfb0O1Jg-ycACw 熟悉 "Pyth ...

  3. Python实现邮件的批量发送

    Python实现邮件的批量发送 1 发送文本信息 '''加密发送文本邮件''' def sendEmail(from_addr,password,to_addr,smtp_server): try: ...

  4. 使用Python轻松批量压缩图片

    在互联网,图片的大小对一个网站的响应速度有着明显的影响,因此在提供用户预览的时候,图片往往是使用压缩后的.如果一个网站图片较多,一张张压缩显然很浪费时间.那么接下来,我就跟大家分享一个批量压缩图片的方 ...

  5. python模块:网络协议和支持

    python模块:网络协议和支持 webbrowser 调用浏览器显示html文件 webbrowser.open('map.html') [webbrowser - Convenient Web-b ...

  6. [原创]开源跨平台大型网络端口扫描器K8PortScan(支持批量A段/B段/C段/IP列表)

    0x000 K8PortScan Python版Cscan端口扫描器 Code: https://github.com/k8gege/K8PortScan K8portScan 1.0 Date: 2 ...

  7. oracle+ibatis 批量插入-支持序列自增

    首先请先看我前面一篇帖子了解oracle批量插入的sql:[oracle 批量插入-支持序列自增] 我用的ibatis2.0,sqlMap文件引入的标签如下: <!DOCTYPE sqlMap ...

  8. oracle 批量插入-支持序列自增

    1.创建表.序列 -- Create table create table test_batch ( id number not null, name ), account ) ) -- Create ...

  9. 解剖SQLSERVER 第十二篇 OrcaMDF 行压缩支持(译)

    解剖SQLSERVER 第十二篇   OrcaMDF 行压缩支持(译) http://improve.dk/orcamdf-row-compression-support/ 在这两个月的断断续续的开发 ...

随机推荐

  1. 详解FL Studio压缩器——Fruity Limiter(上)

    压缩,是电音制作中重要一步,将声音信号压缩后可过滤噪音并使音质变好.众所周知,音乐编曲软件FL Studio的特色就是电音制作,所以必不可少要用到压缩器,今天我们就用FL Studio20来讲解一下. ...

  2. Boom 3D快捷方式,让3D音效应用更便捷

    快捷方式是一种快速启动程序.打开程序功能的方法,巧妙地利用快捷键,可以大大加快我们使用Boom 3D的速度,可以让我们更好地享受3D音效. 接下来,就让小编演示一下怎么在不打开Boom 3D的情况下使 ...

  3. Jmeter(一)发送http请求

    Jmeter中发请求的步骤 1.添加线程组 2.添加http消息头管理器 3.添加http请求 一.线程组: 1.添加路径: 2.字段解释 ①线程数(Number of Threads): : 设置发 ...

  4. 基于Python+requests搭建的自动化框架-实现流程化的接口串联

    框架产生目的:公司走的是敏捷开发模式,编写这种框架是为了能够满足当前这种发展模式,用于前后端联调之前(后端开发完接口,前端还没有将业务处理完毕的时候)以及日后回归阶段,方便为自己腾出学(mo)习(yu ...

  5. 理解与使用Treiber Stack

    目录 背景 名称由来 CompletableFuture源码实现 FutureTask实现 Treiber Stack抽象实现 入栈 出栈 示例 参考 背景 最近在很多JDK源码中都看到了Treibe ...

  6. 工作中用到的redis操作

    del exists 1.字符串 set,get 2.列表 lRange lRem lPush rPush 3.有序列表 zadd zrem zscore 4.hash hset hget hdel

  7. JQuery案例:购物车加减

    购物车加减 <head> <meta charset="UTF-8"> <title>加减购物车</title> <style ...

  8. JDK阅读之Enum

    JDK学习之Enum enum的使用 在没有enum之前如果想要定义一些常量,就会采用如下的方式 假设要定义四个常量表示不同的季节 public class SeasonWithoutEnum { p ...

  9. 冲刺随笔——Day_Ten

    这个作业属于哪个课程 软件工程 (福州大学至诚学院 - 计算机工程系) 这个作业要求在哪里 团队作业第五次--Alpha冲刺 这个作业的目标 团队进行Alpha冲刺 作业正文 正文 其他参考文献 无 ...

  10. JDK8HashMap的一些思考

    JDK8HashMap 文中提及HashMap7的参见博客https://www.cnblogs.com/danzZ/p/14075147.html 红黑树.TreeMap分析详见https://ww ...