Python实现接口下载json文件并指定文件名称
作者:Kwan的解忧杂货铺
文件下载与乱码问题
在 Web 应用中,文件下载通常通过设置 HTTP 响应头中的Content-Disposition来实现。这个响应头告诉浏览器这是一个文件下载,而不是普通的页面内容。然而,如果文件名包含非 ASCII 字符,比如中文、日文或阿拉伯文等,直接设置文件名可能会导致乱码问题。这是因为 HTTP 头默认使用 ASCII 编码,对于非 ASCII 字符,需要进行适当的编码处理。
使用 Python 处理文件下载
在 Python 中,我们可以使用urllib.parse.quote函数来对文件名进行编码,这样可以确保文件名在 HTTP 头中正确传输。此外,为了支持多种浏览器,我们还可以设置filename*参数,使用 RFC 5987 标准来指定文件名的编码。
以下是一个示例代码,展示了如何使用 Python 生成一个带有自定义文件名的 JSON 文件下载响应:
import json import urllib.parse from flask import Response def export_data_as_json(result): # 假设result是一个包含数据的字典 data_ = result['data'] orig_name = data_['orig_name'] # 将数据转换为JSON格式的字符串 json_data = json.dumps(data_) # 对原始文件名进行URL编码,避免URL注入攻击 safe_filename = urllib.parse.quote(orig_name + ".json") # 设置HTTP响应头,指定文件名和编码 headers = { "Content-Disposition": f'attachment; filename="{safe_filename}"; filename*=UTF-8\'{safe_filename}' } # 创建HTTP响应对象 return Response( content=json_data, media_type="application/json", headers=headers )
分析代码实现
在上述代码中,我们首先从result字典中提取出需要导出的数据,并获取原始文件名。然后,我们使用json.dumps将数据转换为 JSON 格式的字符串。
接下来,我们使用urllib.parse.quote函数对原始文件名进行 URL 编码。这一步是必要的,因为它可以防止 URL 注入攻击,并且确保文件名中的特殊字符被正确处理。
在设置 HTTP 响应头时,我们使用了Content-Disposition头,并设置了attachment和filename参数。filename参数指定了文件的默认名称,而filename*参数则使用了 RFC 5987 标准来指定文件名的编码。这样,即使文件名包含非 ASCII 字符,浏览器也能正确解析和显示。
最后,我们创建了一个Response对象,并设置了内容、媒体类型和头部信息,然后返回这个响应对象。当这个响应被发送到客户端时,浏览器会提示用户下载文件,并且文件名会显示为用户自定义的名称,不会出现乱码。
为什么这种方法有效
这种方法有效,因为它遵循了 HTTP 和 Web 浏览器处理文件下载的标准。通过使用filename*
参数,我们告诉浏览器文件名的编码方式,这样浏览器就能正确解码并显示文件名。同时,使用urllib.parse.quote
函数确保了文件名的安全性和兼容性。
拓展:Python如何获取JSON数据的指定标签内容
很多人会用到JSON格式的数据,但是如果想取得JSON数据中的某个键内容,就必须要一层层往下套取才能拿到,如果层次非常多的话,套取将会非常的麻烦。
比如随意编写的JSON内容,想取得相应的值会非常麻烦,在获取请求的数据时,数组内容的数据可能会不同,无法准确定位到底是哪个目录,如果后期目录修改了,我们也需要重新修改操作目录,将会变得非常麻烦。
{ "errcode":0, "errmsg":"ok", "info":{ "sp_no":"202203030015", "sp_name":"风险", "sp_status":3, "template_id":"tetefdfdfdfefsfdfssa", "apply_time":1614759, "applyer":{ "userid":"3940", "partyid":"2" }, "sp_record":[ { "sp_status":3, "approverattr":1, "details":[ { "approver":{ "userid":"3940" }, "speech":"", "sp_status":3, "sptime":1614756, "media_id":[ ] } ] }, { "sp_status":1, "approverattr":1, "details":[ { "approver":{ "userid":"5" }, "speech":"", "sp_status":1, "sptime":0, "media_id":[ ] } ] } ], "notifyer":[ ], "apply_data":{ "contents":[ { "control":"Table", "id":"Table-1614750144354", "title":[ { "text":"测试", "lang":"zh_CN" } ], "value":{ "tips":[ ], "members":[ ], "departments":[ ], "files":[ ], "children":[ { "list":[ { "control":"Date", "id":"Date-1614750", "title":[ { "text":"日期", "lang":"zh_CN" } ], "value":{ "tips":[ ], "members":[ ], "departments":[ ], "date":{ "type":"day", "s_timestamp":"1600000000000" }, "files":[ ], "children":[ ], "stat_field":[ ], "sum_field":[ ], "related_approval":[ ], "students":[ ], "classes":[ ], "docs":[ ], "wedrive_files":[ ] } }, { "control":"Text", "id":"Text-1614", "title":[ { "text":"测试2", "lang":"zh_CN" } ], "value":{ "text":"abcd", "tips":[ ], "members":[ ], "departments":[ ], "files":[ ], "children":[ ], "stat_field":[ ], "sum_field":[ ], "related_approval":[ ], "students":[ ], "classes":[ ], "docs":[ ], "wedrive_files":[ ] } }, { "control":"Textarea", "id":"Textarea-1614", "title":[ { "text":"描述", "lang":"zh_CN" } ], "value":{ "text":"点点滴滴", "tips":[ ], "members":[ ], "departments":[ ], "files":[ ], "children":[ ], "stat_field":[ ], "sum_field":[ ], "related_approval":[ ], "students":[ ], "classes":[ ], "docs":[ ], "wedrive_files":[ ] } }, { "control":"File", "id":"File-16147", "title":[ { "text":"关联附件", "lang":"zh_CN" } ], "value":{ "tips":[ ], "members":[ ], "departments":[ ], "files":[ { "file_id":"fdfdfdafdfdfdsfdassfdfadfaafafasffasfasffsdafsfsffasfsdfas" } ], "children":[ ], "stat_field":[ ], "sum_field":[ ], "related_approval":[ ], "students":[ ], "classes":[ ], "docs":[ ], "wedrive_files":[ ] } }, { "control":"RelatedApproval", "id":"Relatal-161", "title":[ { "text":"关联", "lang":"zh_CN" } ], "value":{ "tips":[ ], "members":[ ], "departments":[ ], "files":[ ], "children":[ ], "stat_field":[ ], "sum_field":[ ], "related_approval":[ { "template_names":[ { "text":"测试", "lang":"zh_CN" } ], "sp_status":1, "name":"张三", "create_time":1611713200, "sp_no":"202201260001" } ], "students":[ ], "classes":[ ], "docs":[ ], "wedrive_files":[ ] } } ] } ], "stat_field":[ ], "sum_field":[ ], "related_approval":[ ], "students":[ ], "classes":[ ], "docs":[ ], "wedrive_files":[ ] } } ] }, "comments":[ { "commentUserInfo":{ "userid":"3940" }, "commenttime":16147, "commentcontent":"是,请xxx ", "commentid":"69358", "media_id":[ ] } ] } }
正常情况下,我们是通过Test['info']['apply_data']['contents'][0]['value']['children'][0]['list'][3]['title'][0]['text']抓取数据,如果抓取数据不存在,则直接报错,当然,我们可以使用get方式去抓取,如果没有直接返回空,这样其实意义不大,所以我后面写了这个代码,可以随意获取相关JSON数据的健对应的值。
它可以直接返回你需要获取的健对应的层级目录,不需要专门去编写相关目录,就能通过该目录获取相关层级的其他目录
比如,我想获取JSON数据中,指定key为:text,值为:关联的附件,然后就能获取当前层级下的节点内容了,其中”['title'][0]['text']“是排除,如果使用则排除这些节点,不使用则返回完整的JSON目录。
dict1 = get_json_to_dict(detail, 'text', '关联附件', len("['title'][0]['text']"))
通过上面的函数就能获取到这个JSON目录"['info']['apply_data']['contents'][0]['value']['children'][0]['list'][3]['title'][0]['text']",如果加了排除,则直接返回这个目录['info']['apply_data']['contents'][0]['value']['children'][0]['list'][3],最终调用处返回这个目录对应的JSON对象内容,非常方便的解决了我们自己写目录的烦恼,也不怕目录改动,因为它能自动找到自己需要的键值的相关目录了
有了这个字典,我就能获取指定字典的相关值,而我编写的这个方法,也能获取相关健值,如获取相关的all_file_id,整体只需要使用两个方法就能解决问题。
如下是具体的代码片段:
loc = locals() # 获取本地资源 def get_var_name(var): """ 通过变量进行获取当前变量的名称 """ for k, v in loc.items(): if loc[k] is var: return k # 后面是具体编写的JSON获取代码 get_key_list = [] # 共用的循环列表 def get_json_key_value(json_object, key_name, key_value): """ 获取相关JSON数据的键值进行对比,并返回相关的节点内容 """ if isinstance(json_object, dict): # 字典 for x in range(len(json_object)): key_tmp = list(json_object.keys())[x] value_tmp = json_object[key_tmp] if len(key_value) == 0: if key_name == key_tmp: get_key_list.append(value_tmp) else: if key_name == key_tmp and key_value == value_tmp: return "['" + key_tmp + "']" result = get_json_key_value(value_tmp, key_name, key_value) # 递归调用 if len(key_value) != 0 and result: return "['" + key_tmp + "']" + result elif isinstance(json_object, list): # 列表 ind = -1 for k in json_object: ind += 1 if isinstance(k, dict): for x in range(len(k)): key_tmp = list(k.keys())[x] value_tmp = k[key_tmp] if len(key_value) == 0: if key_name == key_tmp: get_key_list.append(value_tmp) else: if key_name == key_tmp and key_value == value_tmp: return "[" + str(ind) + "]" + "['" + key_tmp + "']" result = get_json_key_value(value_tmp, key_name, key_value) # 递归调用 if len(key_value) != 0 and result: return "[" + str(ind) + "]" + "['" + key_tmp + "']" + result key_list_tmp = get_key_list.copy() return key_list_tmp def get_json_content(json_obj, key_var, value_var): """ 包装相关JSON返回节点内容,通过键值进行对比,并返回相关的节点内容,如果未找到则返回空 """ key_value = get_json_key_value(json_obj, key_var, value_var) if key_value: return key_value else: return "" def get_json_to_dict(json_obj, key_var, value_var, jump_num): """ 通过键值内容,获取JSON数据对象的相关字典内容,并返回对应的JSON数据对象,可以通过排除方式进行锁定层级对象 """ content = get_json_content(json_obj, key_var, value_var) if content: if jump_num: content = content[0:len(content)-int(jump_num)] try: result = eval(get_var_name(json_obj) + content) except: result = "" else: return "" return result
具体使用代码测试
if __name__ == '__main__': get_key_list = [] # 执行之前清空一下公共数组 dict1 = get_json_to_dict(detail, 'text', '关联附件', len("['title'][0]['text']")) # 获取指定字典内容 all_file_id = get_json_key_value(dict1, "file_id", "") # 获取指定字典中相关的数据健值 print(all_file_id)
到此这篇关于Python实现接口下载json文件并指定文件名称的文章就介绍到这了,更多相关Python下载json并指定文件名称内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!