需求:python脚本通过调用后端接口,实现
定期、自动、上传目录下的所有视频到OSS
一、使用SQLite记录视频数据
思路:使用
SQLite先记录目录下所有文件名称,并添加标识位(0,1)判断是否上传,
例如:未上传是0,已上传是1
下面是python代码
1. 维护一个字典,方便对数据的修改
# 视频源 目录路径
VIDEO_SOURCE_PATH = r'D:\PythonProject\xx\'
# 上传视频接口URL
UPLOAD_URL = 'http://localhost:80/xxx/upload'
# OSS 上传的ACCESS_KEY信息
OSS_ACCESS_VERIFY = {
'access-key-id': 'xx',
'access-key-secret': 'xx',
'bucket-name': 'xx',
'end-point': 'oss-cn-chengdu.aliyuncs.com'
}
# 日志模板
LOG = {
'error': 'error: {} 上传失败!',
'success': 'success: {} 上传成功!',
'null': 'null: {} 上传失败, 文件不存在!',
'has-been-uploaded': '{} 已经存在, 已取消重复上传!'
}
UPLOAD_VIDEO_MESSAGE = {
'no-update': '--执行结果: 暂无新视频上传',
'begin-upload': '=' * 5 + '> (2) 上传视频到 云OSS <' + '=' * 5 + '\n' + 'BEGIN:',
'end-upload': 'END.',
'upload-success': '--success: 上传 {} 成功!',
'upload-error': '--error: 上传 {} 失败!',
'upload-success-response': 'success:upload',
'has-been-uploaded': '上传视频已经存在',
'access-verify-error': '无效的ossAccessKeyVerify',
'no-exist_access': 'OSS_ACCESS_VERIFY配置异常'
}
VIDEO_NAME_TO_SQLITE_MESSAGE = {
'no-update': '--执行结果: 表格暂无更新',
'begin-add': '=' * 5 + '> (1) 保存名称到 SQLite <' + '=' * 5 + '\n' + 'BEGIN:',
'end-add': 'END.',
'add-success': '--success: 添加 {} 成功!',
'add-error': '--error: 添加 {} 失败!',
'permission-deny': 'Permission denied',
'error-notify': '=' * 10 + 'error 注意' + '=' * 10,
'file-is-used': '==文件 {} 正在被使用,请关闭 {} 文件!!!!!==\n',
'create-table-success': '--success: {} 表创建成功'
}
CREATE_VIDEO_MESSAGE_TABLE = """
CREATE TABLE IF NOT EXISTS video_message(
`id` INTEGER PRIMARY KEY AUTOINCREMENT,
`video_name` TEXT,
`monitor_name` TEXT,
`start_time` TEXT,
`end_time` TEXT,
`is_upload` INTEGER)
"""
VIDEO_MESSAGE_SQL = {
'insert': 'INSERT INTO video_message (video_name, monitor_name, start_time, end_time, is_upload) VALUES (?,?,?,?,?)',
'select-video-name': 'SELECT video_name from video_message',
'select-need-upload': 'SELECT * from video_message where is_upload == 0',
'delete-by-id': 'DELETE FROM video_message WHERE id={}',
'update-is-upload': 'UPDATE video_message SET is_upload={} WHERE id={}',
'drop-table': 'DROP TABLE video_message'
}
VIDEO_MAX_SIZE = 1024 * 1024 * 1024
2.添加视频记录到sqlite
因为业务需求,根据视频名称可以获得数据信息:监控源、开始时间、结束时间,因此对名称以
_分隔
class SaveVideoNameToSQLite:
def __init__(self):
"""
初始化 视频源路径
:return:
"""
self.video_source_path = VIDEO_SOURCE_PATH
# 建立连接
self.con = sqlite3.connect("sqlite3_db/upload.db")
# 建立游标
self.cur = self.con.cursor()
# 建立表格
self.cur.execute(CREATE_VIDEO_MESSAGE_TABLE)
def __enter__(self):
return self
def __exit__(self, exc_type, exc_val, exc_tb):
self.cur.close()
self.con.close()
print('添加结束:关闭资源')
@staticmethod
def split_filename(filename):
name, ext = os.path.splitext(filename)
field1, field2, field3 = name.split('_')
return field1, field2, field3
def get_dir_video_name_list(self):
"""
:param : 文件目录路径
:return: 获取目录下的所有文件名
"""
return os.listdir(self.video_source_path)
def get_sqlite_video_name_list(self):
"""
:return list[tuple]:sqlite中的所有视频文件名,
"""
self.cur.execute(VIDEO_MESSAGE_SQL.get('select-video-name'))
return self.cur.fetchall()
def need_add_video_name_list(self):
"""
需要上传的视频文件名列表
:return:
"""
old_list = [name[0] for name in self.get_sqlite_video_name_list()]
dir_list = self.get_dir_video_name_list()
different_list = list(set(dir_list) - set(old_list))
# 排除非mp4文件
return [video for video in different_list if video.endswith('.mp4')]
def delete_table(self):
"""
删除表中所有数据,先删表后重新创建
:return:
"""
self.cur.execute(VIDEO_MESSAGE_SQL.get('drop-table'))
self.cur.execute(CREATE_VIDEO_MESSAGE_TABLE)
self.con.commit()
def add_video_name_to_sqlite(self):
"""
:return:
"""
print(VIDEO_NAME_TO_SQLITE_MESSAGE.get('begin-add'))
need_add_list = self.need_add_video_name_list()
if len(need_add_list) == 0:
print(VIDEO_NAME_TO_SQLITE_MESSAGE.get('no-update'))
print(VIDEO_NAME_TO_SQLITE_MESSAGE.get('end-add'))
return
# 遍历文件名
for video_name in need_add_list:
# 检查文件后缀,如果不是.mp4,则跳过
if video_name[-4:] != '.mp4':
continue
# 拆分文件名得到字段值
monitor_name, start_time, end_time = self.split_filename(video_name)
self.cur.execute(
VIDEO_MESSAGE_SQL.get('insert'), (video_name, monitor_name, start_time, end_time, 0))
print(VIDEO_NAME_TO_SQLITE_MESSAGE.get('add-success').format(video_name))
# 提交
self.con.commit()
print(VIDEO_NAME_TO_SQLITE_MESSAGE.get('end-add'))
sqlite表信息如下
3.上传视频
思路:
(1)扫描sqlite,首先获取is_upload=0的数据,
(2)然后再扫描目录中的数据(确保对应数据存在)
(3)最后遍历is_upload=0的数据,如果存在则上传,上传成功后,修改is_upload=1;反之则清除其在sqlite中的记录;
(4)最后将数据写入日志中
在result中,根据接口返回信息,可以根据自己的需求进行相关操作
由于OSS存储配置了AccessKey,因此需要将参数带到后端
class UploadVideoServer:
def __init__(self):
"""
:return:
excel_source_path: excel路径+excel名称
"""
self.s = requests.session()
self.video_source_path = VIDEO_SOURCE_PATH
# 建立连接
self.con = sqlite3.connect("sqlite3_db/upload.db")
# 建立游标
self.cur = self.con.cursor()
def __enter__(self):
return self
def __exit__(self, exc_type, exc_val, exc_tb):
self.cur.close()
self.con.close()
self.s.close()
print('上传结束:关闭资源')
def get_need_upload_data(self):
self.cur.execute(VIDEO_MESSAGE_SQL.get('select-need-upload'))
return self.cur.fetchall()
def get_real_time_file(self):
"""
获取当前时刻,目录下的 .mp4格式 视频文件名称
:return: list[video_name]
"""
file_list = []
for dir_path, dirs, filenames in os.walk(self.video_source_path):
for file_name in filenames:
if file_name[-4:] != '.mp4':
pass
else:
file_list.append(file_name)
return file_list
def upload_video_to_server(self):
"""
通过requests调用API上传附件和对应信息
:return:
"""
print(UPLOAD_VIDEO_MESSAGE.get('begin-upload'))
need_upload_list = self.get_need_upload_data()
if len(need_upload_list) == 0:
# 如果没有待上传视频,直接退出
print(UPLOAD_VIDEO_MESSAGE.get('no-update'))
print(UPLOAD_VIDEO_MESSAGE.get('end-upload'))
return
real_video_lists = self.get_real_time_file()
# 遍历上传
for datas in need_upload_list:
row_id, video_name, monitor_name, start_time, end_time, is_upload = datas
results = ''
if video_name in real_video_lists:
file_type = 'video/mp4' if os.path.splitext(video_name)[1] == '.mp4' else ''
file_path = self.video_source_path + video_name
headers = {
'tenant-id': '1',
}
if os.path.getsize(file_path) >= VIDEO_MAX_SIZE:
# 文件大小超出1G,退出循环
print(f"--error: 文件大小超出1G,上传失败!")
sys.exit(0)
upload_file = {
'file': (video_name, open(file_path, 'rb'), file_type)
}
# 传给java后端接口数据
datas = {
'videoName': video_name,
'monitorName': monitor_name,
'startTime': '2023-05-01 11:11:11',
'endTime': '2023-05-01 11:11:11',
'ossAccessKeyId': OSS_ACCESS_VERIFY.get('access-key-id'),
'ossAccessKeySecret': OSS_ACCESS_VERIFY.get('access-key-secret'),
'ossBucketName': OSS_ACCESS_VERIFY.get('bucket-name'),
'ossEndPoint': OSS_ACCESS_VERIFY.get('end-point')
}
result = self.s.post(url=UPLOAD_URL, data=datas, files=upload_file, headers=headers)
results = result.text
else:
r = LOG.get('null').format(video_name)
self.write_log(r)
# 如果为空,则删除这条记录excel
self.cur.execute(VIDEO_MESSAGE_SQL.get('delete-by-id').format(row_id))
if UPLOAD_VIDEO_MESSAGE.get('upload-success-response') in results:
r = LOG.get('success').format(video_name)
# 上传成功,修改标识符为 1
self.cur.execute(VIDEO_MESSAGE_SQL.get('update-is-upload').format(1, row_id))
print(UPLOAD_VIDEO_MESSAGE.get('upload-success').format(video_name))
elif UPLOAD_VIDEO_MESSAGE.get('has-been-uploaded') in results:
r = LOG.get('has-been-uploaded').format(video_name)
# 上传视频已经存在,修改标识符为 1
self.cur.execute(VIDEO_MESSAGE_SQL.get('update-is-upload').format(1, row_id))
print(f'--notify: {video_name}已存在!')
elif UPLOAD_VIDEO_MESSAGE.get('access-verify-error') in results:
print(UPLOAD_VIDEO_MESSAGE.get('access-verify-error'))
sys.exit(0)
elif UPLOAD_VIDEO_MESSAGE.get('no-exist_access') in results:
print('--error: ' + UPLOAD_VIDEO_MESSAGE.get('no-exist_access') + "!!!")
sys.exit(0)
else:
r = LOG.get('error').format(video_name)
# 上传失败,不修改文件,下一次遍历时会再上传
print(UPLOAD_VIDEO_MESSAGE.get('upload-error').format(video_name))
self.write_log(r)
# 保存sqlite
self.con.commit()
print(UPLOAD_VIDEO_MESSAGE.get('end-upload'))
@staticmethod
def write_log(r):
"""
记录接口调用日志
:param r: 日志信息
:return:
"""
with open('report/log.txt', 'a', encoding='utf-8') as f:
f.write(r + '\n')
4.函数入口
首先判断相关文件夹是否存在,没有存在则创建
使用with创建上下文环境,确保代码块结束后正确关闭资源
在while循环中,设置没30分钟执行一次
if __name__ == '__main__':
# 判断视频源文件是否存在
if not os.path.exists(VIDEO_SOURCE_PATH):
print(f"严重错误:视频源目录{VIDEO_SOURCE_PATH}不存在,请检查目录是否配置正确!!!")
print(f"严重错误:视频源目录{VIDEO_SOURCE_PATH}不存在,请检查目录是否配置正确!!!")
sys.exit(0)
# 判断文件夹是否存在
file_names = ['sqlite3_db', 'report']
for file_name in file_names:
if not os.path.exists(file_name):
# 创建文件夹
os.makedirs(file_name)
print(f"{file_name}文件夹已创建")
else:
print(f"{file_name}文件夹已存在")
# 开始循环
with UploadVideoServer() as upload_server, SaveVideoNameToSQLite() as save_sqlite:
while True:
print('*' * 20 + 'begin' + '*' * 20)
# 添加新的video_name 到sqlite
save_sqlite.add_video_name_to_sqlite()
# 执行上传
upload_server.upload_video_to_server()
print('*' * 21 + 'end' + '*' * 21)
print('等待下一次上传开始......')
# 暂停30分钟
time.sleep(30 * 60)
5.spring boot后端接口
使用MultipartFile file接收python传来的二进制文件,因为框架中需要上传byte数据,因此做了转化(根据需求转化)
@PostMapping("/upload-oss")
@Operation(summary = "上传视频到OSS")
public CommonResult<String> uploadVideoToOss(@RequestParam("file") MultipartFile file, MonitorVideoCreateReqVO createReqVO, OssAccessVerify ossAccessVerify) {
// 代码逻辑
byte[] content = file.getBytes();
...
}
6.运行结果
(1)添加成功
(2)暂无更新
(3)等待上传
参考文档:
后端详细文档:blog.csdn.net/zhouhengzhe…