【Python 实战】---- 实现一个可选择、配置操作的批量文件上传工具(三)上传类的实现

【Python 实战】---- 实现一个可选择、配置操作的批量文件上传工具(三)上传类的实现

1. 前言

很早之前,我实现过一个【Python 实战】---- 接口自动化:60行代码,如何通过Python requests实现图片上传,这个的确简化了前端开发对图片操作的时间,但是随着项目开发的越多,遇到一个新的问题,就是图片需要上传到每个项目的自己服务器中,而且每个项目的参数也不一样,有的需要加密,有的不需要,这就会出现我们需要根据不同的项目,对代码进行上传的微调,然后再打包成工具使用,最后就会发现有很多工具,完全达不到一个程序员的优雅,因此就想到使用 GUI 配置管理,将所有的工具进行配置管理。上一篇文章只是对事件进行了实现,但是上传类没有实现,现在详细实现上传类。

2. 上传类的调用

ini 复制代码
# 生成icon.js内容但不写入文件
uploader = FileUploader(self.current_config)

3. 递归获取文件夹下所有指定后缀的文件

3.1 实现分析

  1. 初始化一个空的结果列表 results 用于存储找到的文件路径。
  2. 使用 os.walk() 方法递归遍历给定目录 dir_path 下的所有文件和子目录。
  3. 对于遍历到的每个文件,将其路径与目录路径拼接成完整的文件路径。
  4. 使用 os.path.splitext() 方法获取文件的后缀名,并去掉开头的点号(.)。
  5. 检查文件后缀名是否在传入的后缀名列表 suffixes 中,如果匹配则将该文件的完整路径添加到结果列表中。
  6. 在遍历过程中如果发生异常,则打印异常信息。
  7. 最后返回包含所有匹配文件完整路径的列表。

3.2 实现代码

python 复制代码
def get_files_with_suffix(self, dir_path, suffixes):
    """递归获取文件夹下所有指定后缀的文件"""
    results = []
    try:
        for root, dirs, files in os.walk(dir_path):
            for file in files:
                file_path = os.path.join(root, file)
                if os.path.splitext(file_path)[1][1:] in suffixes:
                    results.append(file_path)
    except Exception as e:
        print(e)
    return results

4. 上传单个文件

4.1 实现分析

  1. 使用二进制模式打开指定路径的文件。
  2. 构造上传文件的参数,包括文件字段名、文件名和文件内容,以及文件类型。
  3. 从配置中获取额外的数据字段,如果未配置则使用空字典,并复制该字典以避免修改原始配置。
  4. 向数据字典中添加必要的字段,包括文件类型字段和名称字段。
  5. 使用 requests 库发送 POST 请求到配置中的 URL,携带配置的请求头、数据和文件参数。
  6. 检查响应状态码是否为 200(成功)。
  7. 如果状态码为 200,解析响应的 JSON 数据并检查其中的 code 字段是否与配置中的成功码匹配。
  8. 如果上传成功,返回响应数据;如果上传失败或响应码不匹配,打印错误信息并返回 None
  9. 在整个过程中如果发生异常,捕获异常并打印错误信息,然后返回 None

4.2 实现代码

python 复制代码
def upload_file(self, file_path, file_type):
    """上传单个文件"""
    try:
        with open(file_path, 'rb') as f:
            files = {self.config.file_field_name: (os.path.basename(file_path), f, f"image/{file_type}")}
            # 使用配置中的data字段,如果未配置则使用默认值
            data = getattr(self.config, 'data', {})
            # 合并必要的字段
            data = data.copy()  # 创建副本以避免修改原始配置
            data[self.config.file_type_field] = file_type
            data[self.config.name_field] = self.config.name_value
            res = requests.post(url=self.config.url, headers=self.config.headers, data=data, files=files)
            if res.status_code == 200:
                response_data = res.json()
                if response_data.get("code") == self.config.success_code:
                    return response_data
                else:
                    print(f"Failed to upload {file_path}: {response_data}")
                    return None
            else:
                print(f"Failed to upload {file_path}: HTTP {res.status_code}")
                return None
    except Exception as e:
        print(f"Error uploading {file_path}: {e}")
        return None

5. 批量上传图片

5.1 实现分析

  1. 首先清空上一次的上传日志文件 'upload.log'。
  2. 调用 get_files_with_suffix 方法获取目录下所有匹配后缀名的文件路径列表。
  3. 初始化一个空的结果列表 results 用于存储上传成功的文件信息。
  4. 遍历文件路径列表,对每个文件执行以下操作:
    • 计算文件相对于上传目录的相对路径,用作显示名称。
    • 生成上传日志信息,并通过回调函数显示在界面上,同时写入日志文件。
    • 获取文件的后缀名(文件类型)。
    • 调用 upload_file 方法上传文件。
    • 如果上传成功,将文件相对路径和上传结果添加到结果列表中,生成成功日志信息并通过回调函数显示,同时写入日志文件。
    • 如果上传失败,生成失败日志信息并通过回调函数显示,同时写入日志文件。
  5. 返回包含所有上传成功文件信息的结果列表。

5.2 实现代码

python 复制代码
def upload_images(self, dir_path, suffixes, log_callback=None):
    """批量上传图片"""
    # 清空上一次的日志
    open('upload.log', 'w').close()
    
    files = self.get_files_with_suffix(dir_path, suffixes)
    results = []

    for file_path in files:
        relative_path = os.path.relpath(file_path, dir_path)
        
        # 使用原文件名
        display_name = relative_path
        
        log_message = f"Uploading {display_name}...\n"
        if log_callback:
            log_callback(log_message.strip())
        # 将日志写入文件
        with open('upload.log', 'a', encoding='utf-8') as log_file:
            log_file.write(log_message)
            
        file_type = os.path.splitext(file_path)[1][1:]
        result = self.upload_file(file_path, file_type)
        if result:
            results.append({
                "fileName": relative_path,
                "result": result
            })
            success_message = f"Uploaded {display_name} successfully\n"
            if log_callback:
                log_callback(success_message.strip())
            # 将成功日志写入文件
            with open('upload.log', 'a', encoding='utf-8') as log_file:
                log_file.write(success_message)
        else:
            fail_message = f"Failed to upload {display_name}\n"
            if log_callback:
                log_callback(fail_message.strip())
            # 将失败日志写入文件
            with open('upload.log', 'a', encoding='utf-8') as log_file:
                log_file.write(fail_message)
    return results

6. 上传结果写入 icon.js 文件

6.1 实现分析

  1. 初始化数据结构 :创建一个空字典 icon_data 用于存储处理后的图片信息。
  2. 处理上传结果 :遍历传入的 results 列表(包含上传文件的结果信息):
    • 检查每个文件的上传结果,只有当返回的 code 与配置中的 success_code 匹配时才继续处理。
    • 提取文件名(不含路径)并去除扩展名,生成基础的 key 名称(格式为 文件名Icon)。
    • 如果配置中设置了前缀(prefix),则将前缀和 key 都转换为驼峰命名格式,并组合成最终的 key。
    • 获取图片路径前缀(从界面输入框 img_prefix_entry 获取)。
    • 取出上传结果中的实际图片路径值(由配置中的 content_field 指定字段)。
    • 如果设置了图片路径前缀且原始路径值不为空,则将前缀与原始路径拼接;否则直接使用原始路径值。
    • 将处理好的 key-value 对存入 icon_data 字典中。
  3. 生成文件内容 :按照 JavaScript 对象的格式,将 icon_data 中的所有键值对构造成一个字符串,格式为 export default { "key": "value", ... }
  4. 确定文件路径 :如果传入了有效的 dir_path 参数,则将 icon.js 文件保存在该目录下;否则默认保存在当前目录(./icon.js)。
  5. 写入文件 :尝试以 UTF-8 编码将构造好的内容写入到 icon.js 文件中。如果写入成功返回 True,若出现异常则打印错误信息并返回 False

6.2 实现代码

python 复制代码
def write_results_to_icon_js(self, results, dir_path=None):
    """将上传结果写入 icon.js 文件"""
    icon_data = {}

    # 构建 icon_data 对象
    for item in results:
        if item["result"].get("code") == self.config.success_code:
            # 使用原文件名生成key
            file_name = os.path.basename(item["fileName"])
            file_name_without_ext = os.path.splitext(file_name)[0]
            
            # 生成key
            key = f"{file_name_without_ext}Icon"
            
            # 添加前缀(如果配置了前缀)并转换为驼峰命名
            prefix = getattr(self.config, 'prefix', '')
            if prefix:
                # 将前缀和key转换为驼峰命名
                prefix = self.to_camel_case(prefix)
                key = self.to_camel_case(key)
                key = f"{prefix}{key}"
            
            # 获取图片路径前缀
            img_prefix = self.img_prefix_entry.get() if hasattr(self, 'img_prefix_entry') else ''
            
            # 获取原始值
            original_value = item["result"].get(self.config.content_field)
            
            # 如果有图片路径前缀且原始值不为空,则拼接前缀
            if img_prefix and original_value:
                icon_data[key] = f"{img_prefix}{original_value}"
            else:
                icon_data[key] = original_value

    # 生成文件内容
    content = "export default {\n"
    for key, value in icon_data.items():
        content += f'  "{key}": "{value}",\n'
    content += "}\n"

    # 确定文件路径
    if dir_path and os.path.exists(dir_path):
        file_path = os.path.join(dir_path, 'icon.js')
    else:
        file_path = './icon.js'

    # 写入文件
    try:
        with open(file_path, 'w', encoding='utf-8') as f:
            f.write(content)
        return True
    except Exception as e:
        print(f"Error writing to icon.js: {e}")
        return False

7. 工具函数【字符串转换为驼峰命名】

7.1 实现分析

  1. 首先使用正则表达式 re.sub(r'[^a-zA-Z0-9_]', '', text) 移除字符串中的所有特殊字符,仅保留字母、数字和下划线。
  2. 然后通过下划线 '_' 对处理后的字符串进行分割,得到一个单词列表。
  3. 最后,将列表中的第一个单词保持小写,后续所有单词的首字母大写并拼接在一起,形成最终的驼峰命名格式字符串。例如,输入 "hello_world_example" 会转换为 "helloWorldExample"

7.2 实现代码

python 复制代码
def to_camel_case(self, text):
    """将字符串转换为驼峰命名格式"""
    # 移除特殊字符,只保留字母、数字和下划线
    import re
    text = re.sub(r'[^a-zA-Z0-9_]', '', text)
    
    # 按下划线分割并转换为驼峰命名
    parts = text.split('_')
    return parts[0] + ''.join(part.capitalize() for part in parts[1:])

8. 总结

  1. 上传类的方法基本都实现完成,需要处理一些细节问题。
  2. 就是单文件上传后边准备转为外部文件实现,此处读取实现上传后返回的结果,这样配置就不需要那么麻烦,而且也解决了有的平台上传是参数加密问题,直接执行外部脚本,此单文件上传只需要关心脚本返回的结果。
相关推荐
WBluuue36 分钟前
数学建模:智能优化算法
python·机器学习·数学建模·爬山算法·启发式算法·聚类·模拟退火算法
赴3351 小时前
矿物分类案列 (一)六种方法对数据的填充
人工智能·python·机器学习·分类·数据挖掘·sklearn·矿物分类
大模型真好玩1 小时前
一文深度解析OpenAI近期发布系列大模型:意欲一统大模型江湖?
人工智能·python·mcp
RPA+AI十二工作室1 小时前
亚马逊店铺绩效巡检_影刀RPA源码解读
chrome·python·rpa·影刀
小艳加油2 小时前
Python机器学习与深度学习;Transformer模型/注意力机制/目标检测/语义分割/图神经网络/强化学习/生成式模型/自监督学习/物理信息神经网络等
python·深度学习·机器学习·transformer
学行库小秘4 小时前
ANN神经网络回归预测模型
人工智能·python·深度学习·神经网络·算法·机器学习·回归
Yn3124 小时前
在 Python 中使用 json 模块的完整指南
开发语言·python·json
秋难降4 小时前
线段树的深度解析(最长递增子序列类解题步骤)
数据结构·python·算法
猿榜4 小时前
Python基础-控制结构
python