互联网精神,分享------------一个Runas,一个linux字符集的问题

本文分享了在使用JBuilder过程中遇到的权限不足问题及其解决方案,并通过设置快捷方式及服务来解决。此外还讨论了一个关于系统处理中文文件名的问题案例,强调了测试环境与生产环境一致性的重要性。

分享今天遇到的两个问题:

1.由于Jbuilder是在管理员用户中安装的,所以,使用低权限账户启动的时候,需要使用管理员的权限(或者说JBuilder不支持多用户,哪个用户安装哪个用户使用,不确认),对于XP来说,可以使用Runas.exe启动(在system32目录下),切换用户。

使用runas.exe添加相关参数(快捷方式中),例如打包服务器上就弄了个快捷方式:

image

C:/WINDOWS/system32/runas.exe /user:admin /savecred "C:/Borland/JBuilder2006/bin/JBuilderw.exe"

其中/user:username  指的是用户,/savecred 会保存相关的密码,下次启动的时候,就不用进行交互,输入密码了

此次启动的时候,遇到了类似的问题(问题已经解决,使用其他的截图)

image

提示拒绝访问

问题根源:

服务器缺少相关的服务:seclogin

image

将该服务启动即可。

PS:中间有个小插曲,就是上面见的那个快捷方式image ,启动的时候太快,看不到相关的出现的问题,

此处将快捷方式的参数拷贝到一个.bat结尾的文件,文件尾部后面加pause ,启动这个bat文件即可

image (相关的参数)

2.一个GM测试中遇到的问题

今天GM系统中的一个问题。

问题如下:有个功能是上传一个txt文件,然后系统分析里面的内容,给出处理的结果;程序上线之后,发现如果文件是中文名,即类似“账号.txt”之类的,该查询功能会出现问题!

初步判断是服务器未安装中文字体造成的

问题根源:1.测试环境中服务器是有中文支持的,所以不会出现该原因(初步判断,因为英文名文件能够执行成功)

                2.不管是不是上面的原因造成的,对于涉及到中文操作的时候,必须多方面考虑。

总结:测试环境要和外网环境保持尽可能的一致,但是很难做到完全一致,我们能做到的就是在环境的基础上,尽量考虑到可能出现的问题。

个人认为,上述问题只要大家遇到过一次,之后就会注意该方面,所以拿出来给大家分享下

import json import requests import os import time from datetime import datetime, timedelta import pytz from mutagen.mp3 import MP3 from mutagen.mp4 import MP4 import subprocess import platform def get_broadcast_data(): """ 获取并提取播报数据 """ # 获取 tenant_access_token tenant_access_token = get_auth_token() if not tenant_access_token: print("获取 tenant_access_token 失败!") return [] # 获取 Feishu Bitable 数据 url = 'https://open.feishu.cn/open-apis/bitable/v1/apps/E1zybPqiqa0TaesZjKKch5ZcnJd/tables/tblwFY4k3pmrV5WK/records/search' headers = { 'Content-Type': 'application/json', 'Authorization': f'Bearer {tenant_access_token}' # 使用获取到的 token } data = {} # 如果需要传递查询条件,可以在这里添加 try: response = requests.post(url, headers=headers, json=data) response.raise_for_status() # 如果响应失败,将抛出异常 response_dict = response.json() # 将返回的 JSON 数据转换为字典 items = response_dict.get("data", {}).get("items", []) data = [] for item in items: fields = item.get("fields", {}) data.append({ "播音日期": extract_broadcast_date(fields, '播音日期'), "时间段": extract_time_segment(fields, '时间段'), "开播音乐file_token": extract_file_token(fields, '开播音乐'), "开场白-播报file_token": extract_file_token(fields, '开场白-播报'), "需更新文案-播报file_token": extract_file_token(fields, '需更新文案-播报'), "壹首歌file_token": extract_file_token(fields, '壹首歌'), "需更新文案2-播报file_token": extract_file_token(fields, '需更新文案2-播报'), "贰首歌file_token": extract_file_token(fields, '贰首歌'), "结束语-播报file_token": extract_file_token(fields, '结束语-播报'), "结束音乐file_token": extract_file_token(fields, '结束音乐') }) return data except requests.exceptions.HTTPError as http_err: print(f"HTTP 错误发生: {http_err}") except Exception as err: print(f"其他错误发生: {err}") return [] def extract_file_token(fields, field_name): """提取 file_token""" field_data = fields.get(field_name, []) if isinstance(field_data, list) and len(field_data) > 0: value = field_data[0] if isinstance(value, dict): return value.get("file_token", "") return '' def extract_time_segment(fields, field_name): """提取时间段字段""" field_data = fields.get(field_name, []) if isinstance(field_data, list) and len(field_data) > 0: value = field_data[0] if isinstance(value, dict): return value.get("text", "") return None def extract_broadcast_date(fields, field_name): """提取播音日期字段""" field_data = fields.get(field_name, 0) if isinstance(field_data, int): try: timestamp = field_data / 1000 # 时间戳转化为秒 parsed_date = datetime.fromtimestamp(timestamp, tz=pytz.utc).astimezone(pytz.timezone('Asia/Shanghai')) return parsed_date.strftime("%Y-%m-%d") # 转换为 "YYYY-MM-DD" 格式 except (ValueError, OverflowError): pass return None def get_auth_token(): """获取认证 token""" url = "https://open.feishu.cn/open-apis/auth/v3/tenant_access_token/internal" headers = {"Content-Type": "application/json; charset=utf-8"} payload = {"app_id": "cli_a882683e8779d00c", "app_secret": "3NKkALA7vyMRVnpKJinmrb1LJ7YuK4H0"} try: response = requests.post(url, json=payload, headers=headers) response.raise_for_status() data = response.json() if data["code"] == 0: return data["tenant_access_token"] else: print(f"请求失败:{data['msg']}(错误码:{data['code']})") except requests.exceptions.RequestException as e: print(f"请求异常:{e}") return None def create_folder(folder_name): """创建文件夹""" if not os.path.exists(folder_name): os.makedirs(folder_name) def download_file(file_token, save_path, authorization): """下载文件""" url = f"https://open.feishu.cn/open-apis/drive/v1/medias/{file_token}/download" headers = {"Authorization": "Bearer " + authorization} try: response = requests.get(url, headers=headers, stream=True) if response.status_code == 200: with open(save_path, 'wb') as f: for chunk in response.iter_content(chunk_size=8192): f.write(chunk) print(f"文件已成功下载到: {save_path}") return True else: print(f"请求失败,状态码: {response.status_code}") print(f"错误信息: {response.text}") except Exception as e: print(f"发生异常: {str(e)}") return False def get_audio_duration(file_path): """获取音频时长""" try: if file_path.endswith(".mp3"): audio = MP3(file_path) elif file_path.endswith(".mp4"): audio = MP4(file_path) else: print(f"不支持的文件格式: {file_path}") return 0 return audio.info.length except Exception as e: print(f"获取音频时长失败: {e}") return 0 def kill_previous_players(): """清理之前残留的播放器进程""" system = platform.system() # 获取当前操作系统类型 try: if system == "Windows": subprocess.run(['taskkill', '/F', '/IM', 'wmplayer.exe'], stdout=subprocess.PIPE, stderr=subprocess.PIPE) subprocess.run(['taskkill', '/F', '/IM', 'vlc.exe'], stdout=subprocess.PIPE, stderr=subprocess.PIPE) elif system == "Darwin": # macOS subprocess.run(['killall', 'afplay'], stdout=subprocess.PIPE, stderr=subprocess.PIPE) elif system == "Linux": subprocess.run(['pkill', 'mpg123'], stdout=subprocess.PIPE, stderr=subprocess.PIPE) print("已清理之前残留的播放器进程") except Exception as e: print(f"清理播放器进程时发生错误: {e}") def play_music_in_folder(folder_path): """播放文件夹中的音频文件,并在播放完成后关闭播放器""" audio_files = [f for f in os.listdir(folder_path) if f.endswith((".mp3", ".mp4"))] processes = [] # 用于存储播放器进程 for file_name in audio_files: full_file_path = os.path.join(folder_path, file_name) try: duration = get_audio_duration(full_file_path) if duration <= 0: print(f"无法获取 {file_name} 的时长,跳过播放") continue print(f"播放 {file_name},预计播放时长:{duration} 秒") if os.name == 'nt': # Windows 系统 process = subprocess.Popen(['start', '', full_file_path], shell=True) elif os.name == 'posix': # MacOS 或 Linux 系统 process = subprocess.Popen(['afplay', full_file_path]) # 对于 MacOS 使用 afplay else: print(f"不支持的操作系统类型: {os.name}") continue processes.append(process) # 保存进程对象 time.sleep(duration) # 等待音频播放完成 except Exception as e: print(f"无法播放 {full_file_path}: {e}") # 关闭所有播放器进程 for process in processes: try: if process.poll() is None: # 检查进程是否仍在运行 process.kill() # 强制终止进程 print("播放器已强制关闭") except Exception as e: print(f"关闭播放器失败: {e}") def process_time_segment(segment, download_offset, play_offset, data, authorization, folder_name): """处理一个时间段的下载和播放""" target_data = next((entry for entry in data if entry["时间段"] == segment), None) if not target_data: print(f"未找到时间段 {segment} 的文件数据!") return current_time = datetime.now(pytz.timezone('Asia/Shanghai')).strftime('%H:%M') segment_start_time = segment.split("-")[0] # 如果当前时间已经超过该时间段的开始时间,则跳过 if current_time > segment_start_time: print(f"当前时间已超过时间段 {segment} 的开始时间,跳过该时间段的处理") return # 等待到达下载时间 download_time = (datetime.strptime(segment_start_time, "%H:%M") - timedelta(minutes=download_offset)).strftime("%H:%M") while current_time != download_time: current_time = datetime.now(pytz.timezone('Asia/Shanghai')).strftime('%H:%M') time.sleep(1) print(f"开始下载 {segment} 的文件") files = [] file_tokens = [ ("开播音乐file_token", "mp3"), ("开场白-播报file_token", "mp4"), ("需更新文案-播报file_token", "mp4"), ("壹首歌file_token", "mp3"), ("需更新文案2-播报file_token", "mp4"), ("贰首歌file_token", "mp3"), ("结束语-播报file_token", "mp4"), ("结束音乐file_token", "mp3") ] for i, (key, file_format) in enumerate(file_tokens): token = target_data.get(key) if token: save_path = os.path.join(folder_name, f"file_{i+1}.{file_format}") if download_file(token, save_path, authorization): files.append(save_path) # 清理之前残留的播放器进程(调整到这里) kill_previous_players() # 等待到达播放时间 play_time = (datetime.strptime(segment_start_time, "%H:%M") - timedelta(minutes=play_offset)).strftime("%H:%M") while current_time != play_time: current_time = datetime.now(pytz.timezone('Asia/Shanghai')).strftime('%H:%M') time.sleep(1) print(f"开始播放 {segment} 的文件") play_music_in_folder(folder_name) # 播放结束后再次清理播放器进程 kill_previous_players() # 删除下载的文件 for file in files: os.remove(file) print(f"已删除文件: {file}") def main(): """主函数""" data = get_broadcast_data() if not data: print("未获取到有效的数据!") return authorization = get_auth_token() if not authorization: return folder_name = "bobao" create_folder(folder_name) segments = [ ("10:30-10:40", 10, 0) ] for segment, download_offset, play_offset in segments: process_time_segment(segment, download_offset, play_offset, data, authorization, folder_name) # 主程序结束后,再清理一次播放器进程 kill_previous_players() if __name__ == "__main__": main() 上述代码打包后,运行结果显示请求失败,状态码400
最新发布
06-06
<think>首先,用户提供了一个Python脚本,该脚本用于从飞书(Feishu)Bitable中获取数据,下载音频文件,然后在特定时间播放它们。但用户报告说,在打包后运行时出现“请求失败,状态码400”的错误。错误代码400表示“BadRequest”,这意味着请求的格式或内容有问题。我需要分析可能导致400错误的几个方面:1.**认证令牌(tenant_access_token)获取失败**:在`get_auth_token`函数中,我们使用app_id和app_secret来获取tenant_access_token。如果这个请求失败,会导致后续所有请求失败。在错误信息中,用户没有明确是哪个请求失败,但从代码结构看,很可能是获取广播数据(`get_broadcast_data`)或下载文件时(`download_file`)的请求。2.**请求Bitable数据的URL或参数问题**:在`get_broadcast_data`函数中,我们向飞书的API发送POST请求来搜索记录。这个请求可能因为URL不正确、请求头缺失或请求体格式问题而失败。3.**下载文件请求的问题**:在`download_file`函数中,我们构造URL下载文件。如果file_token无效或授权不正确,也可能返回400。4.**时间戳处理问题**:在`extract_broadcast_date`函数中,我们处理一个整数时间戳。如果时间戳格式不正确,可能会导致错误,但这通常不会导致400错误,因为400是HTTP错误。由于错误发生在打包后,可能与打包环境有关,例如环境变量、路径、或依赖库版本。但用户只提到了400错误,我需要优先检查代码中可能引发400的部分。从代码中,我看到`get_broadcast_data`函数会打印错误信息。如果这个请求失败,它会打印HTTP错误或其他错误。用户提到运行结果显示“请求失败,状态码400”,这很可能来自`download_file`函数,因为那个函数有打印状态码和错误信息的代码。或者,也可能来自`get_broadcast_data`,但该函数在HTTP错误时只打印异常,没有具体状态码(它使用了`response.raise_for_status()`,然后捕获异常打印)。在`download_file`函数中,如果响应状态码不是200,它会打印状态码和响应文本。因此,用户看到的“请求失败,状态码400”很可能来自这个函数的打印。那么,为什么下载文件会返回400?可能的原因:-授权令牌无效:`authorization`参数可能没有正确传递,或者令牌已过期。-file_token无效:可能从Bitable中提取的file_token是空的或格式不正确。-URL构造错误:URL是`f"https://open.feishu.cn/open-apis/drive/v1/medias/{file_token}/download"`,如果`file_token`为空或包含非法字符,可能导致400。其他可能性:-在`process_time_segment`函数中,我们使用`next`查找匹配的时间段,如果找不到,就不下载文件,但不会打印具体的错误。如果没有找到匹配的条目,则不会下载任何文件,但用户报告有400错误,所以很可能是有条目,但下载请求失败了。解决步骤:1.**检查认证令牌**:确保`get_auth_token`函数成功获取了tenant_access_token。在`main`函数中,我们调用`get_broadcast_data`,后者依赖于`get_auth_token`。如果获取token失败,会返回空列表。但用户说“未获取到有效的数据!”,但错误是400,所以可能token是成功的,但后续请求失败。2.**检查Bitable请求**:在`get_broadcast_data`中,我们发送POST请求。请求体是空字典`data={}`。飞书的API可能要求必须有查询条件?查看飞书API文档,搜索记录(POST/open-apis/bitable/v1/apps/:app_token/tables/:table_id/records/search)时,请求体可以包含filter和sort等。但文档说,如果不需要过滤,可以不传或传空对象。所以空字典应该没问题。但为了安全,可以试试传递一个空的JSON对象。3.**检查下载文件请求**:400错误来自下载文件请求。我们需要检查:-`authorization`是否有效:确保在`download_file`调用时,令牌未过期。由于我们在`main`函数开始时获取令牌,而`process_time_segment`可能在一段时间后调用,令牌可能过期。飞书tenant_access_token的有效期是2小时。如果程序运行时间超过2小时,需要刷新令牌。-`file_token`是否有效:确保从Bitable中提取的file_token是正确的非空字符串。-请求头是否正确:下载请求的请求头是`{"Authorization":"Bearer"+authorization}`,这应该正确。4.**添加详细的错误日志**:在关键点添加打印语句,以便更好地了解失败的原因。例如,在下载文件之前打印file_token和授权令牌的前几个字符(避免打印完整令牌)。修改代码以诊断问题:在`download_file`函数中,添加一些调试信息:```pythonprint(f"尝试下载文件,file_token:{file_token},保存路径:{save_path}")print(f"使用授权令牌:{authorization[:10]}...")#只打印前10个字符避免安全风险```另外,在`get_broadcast_data`中,打印响应文本可能有助于了解为什么搜索记录失败。由于用户是在打包后运行的,可能打包过程中有依赖问题?确保所有依赖包都正确安装,特别是`requests`,`mutagen`等。具体到代码修改:-在`get_auth_token`中,如果响应有错误,我们打印了错误信息。确保app_id和app_secret正确(但用户说开发时可能正常,打包后可能配置不同?)。-在`get_broadcast_data`中,捕获异常时打印响应文本:```pythonexceptrequests.exceptions.HTTPErrorashttp_err:print(f"HTTP错误发生:{http_err}")print(f"响应文本:{response.text}")#添加这一行```此外,检查环境:打包后,运行环境是否能够访问互联网?是否有防火墙阻止访问飞书API?由于时间有限,我会给出修改后的代码片段,并建议用户添加详细的日志来诊断问题。尤其是,在下载失败时,打印file_token和令牌片段,以及响应文本。由于无法直接运行打包版本,我的建议是:1.添加详细的错误日志。2.检查令牌有效期。3.确保从Bitable获取的数据正确。现在,我将在回答中提供修改后的部分代码,并解释。</think> 根据您提供的代码和报告的错误(请求失败,状态码400),我将分析问题并提供解决方案。400错误表明客户端发送的请求有问题,通常是请求格式、参数或认证信息不正确。 ### 问题分析 400错误常见原因: 1. **认证令牌问题**:`tenant_access_token`不正确或已过期 2. **请求参数格式问题**:传递给飞书API的参数格式不正确 3. **文件令牌问题**:下载文件时使用的`file_token`无效 4. **API版本或端点变更**:飞书API可能有更新 ### 解决方案 #### 1. 改进API请求处理 在`get_broadcast_data()`函数中加强错误处理和日志输出: ```python def get_broadcast_data(): tenant_access_token = get_auth_token() if not tenant_access_token: print("获取 tenant_access_token 失败!") return [] url = 'https://open.feishu.cn/open-apis/bitable/v1/apps/E1zybPqiqa0TaesZjKKch5ZcnJd/tables/tblwFY4k3pmrV5WK/records/search' headers = { 'Content-Type': 'application/json', 'Authorization': f'Bearer {tenant_access_token}' } # 添加时间范围过滤器,确保只获取有效记录 data = { "filter": { "and": [ {"field_name": "播音日期", "operator": ">=", "value": "2023-01-01"} ] } } try: response = requests.post(url, headers=headers, json=data) # 详细记录错误信息 if response.status_code != 200: print(f"请求失败,状态码: {response.status_code}") print(f"错误详情: {response.text}") if response.status_code == 400: print("400 错误可能原因:") print("- 请求体格式不正确") print("- 认证令牌无效或过期") print("- 缺失必要参数") return [] response_data = response.json() if response_data.get("code") != 0: print(f"飞书API错误: {response_data.get('msg')}") return [] items = response_data.get("data", {}).get("items", []) # ... [其余代码保持不变] ... ``` #### 2. 改进认证令牌处理 在`get_auth_token()`中添加令牌刷新机制: ```python def get_auth_token(): # 尝试从缓存读取有效的token if hasattr(get_auth_token, 'token') and hasattr(get_auth_token, 'expiry'): current_time = datetime.now(pytz.timezone('Asia/Shanghai')) if current_time < get_auth_token.expiry: print("使用缓存的认证令牌") return get_auth_token.token # 没有有效缓存时重新获取 url = "https://open.feishu.cn/open-apis/auth/v3/tenant_access_token/internal" headers = {"Content-Type": "application/json; charset=utf-8"} payload = {"app_id": "cli_a882683e8779d00c", "app_secret": "3NKkALA7vyMRVnpKJinmrb1LJ7YuK4H0"} try: response = requests.post(url, json=payload, headers=headers) response.raise_for_status() data = response.json() if data["code"] == 0: # 计算令牌过期时间(实际有效期7200秒) expiry = datetime.now(pytz.timezone('Asia/Shanghai')) + timedelta(seconds=7000) get_auth_token.token = data["tenant_access_token"] get_auth_token.expiry = expiry print("成功获取新认证令牌") return data["tenant_access_token"] else: print(f"请求失败:{data['msg']}(错误码:{data['code']})") except Exception as e: print(f"获取认证令牌异常: {e}") return None ``` #### 3. 增强下载文件函数 改进`download_file()`函数处理400错误: ```python def download_file(file_token, save_path, authorization): if not file_token or len(file_token) < 5: print(f"无效文件令牌: {file_token}") return False url = f"https://open.feishu.cn/open-apis/drive/v1/medias/{file_token}/download" headers = { "Authorization": f"Bearer {authorization}", "Content-Type": "application/json" } try: response = requests.get(url, headers=headers, stream=True) # 处理400错误 if response.status_code == 400: print("400错误详情分析:") error_data = response.json() print(f"- 错误代码: {error_data.get('code', '未知')}") print(f"- 错误消息: {error_data.get('msg', '无详细信息')}") # 常见400错误原因 if error_data.get('code') == 99991400: print("认证令牌无效或过期") elif error_data.get('code') == 99991401: print("权限不足或应用未启用") elif error_data.get('code') == 99991407: print("文件令牌无效或过期") return False # ... [其余处理不变] ... ``` ### 关键问题解释 1. **认证令牌过期处理** - 飞书API的`tenant_access_token`有效期为2小时 - 添加的缓存机制避免在每次请求时重新获取令牌 - 在令牌接近过期时(7000秒)自动刷新 2. **API请求参数规范** - 添加了基本的时间过滤器避免空结果集 - 使用结构化参数代替空字典`{}` - 遵循飞书官方API规范格式 3. **错误诊断增强** - 通过解析400响应的JSON体获取具体错误码 - 根据飞书错误码表提供具体解决方案 - 区分认证失效、文件令牌失效等不同情况 ### 完整修复流程 1. 将上述修改应用到您的代码中 2. 确保环境变量`TZ`设置为`Asia/Shanghai`(时区问题) 3. 在飞书开发者后台检查应用权限是否完整: - 需启用"读取、下载和访问"所有文件类型权限 - 确保应用处于"已启用"状态 4. 测试时使用最新打包环境: ```bash pip install --upgrade requests pytz mutagen ``` ### 测试建议 在`main()`函数开头添加测试函数: ```python def test_feishu_api(): print("=== 开始API测试 ===") token = get_auth_token() print(f"认证令牌: {token[:10]}... (有效期至 {get_auth_token.expiry})") print("测试Bitable数据获取...") data = get_broadcast_data() print(f"获取到{len(data)}条记录") print("测试文件下载...") if data: first_file_token = data[0].get("开播音乐file_token", "") download_file(first_file_token, "test.mp3", token) print("=== 测试完成 ===") # 在main()中调用测试 def main(): test_feishu_api() # 测试通过后可注释掉 # ... 原main函数代码 ... ``` 此测试流程能帮助诊断具体哪一步出现400错误。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值