Python實現接口下載json文件并指定文件名稱
文件下載與亂碼問題
在 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}'
}
# 創(chuàng)建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 字符,瀏覽器也能正確解析和顯示。
最后,我們創(chuàng)建了一個Response對象,并設置了內容、媒體類型和頭部信息,然后返回這個響應對象。當這個響應被發(fā)送到客戶端時,瀏覽器會提示用戶下載文件,并且文件名會顯示為用戶自定義的名稱,不會出現亂碼。
為什么這種方法有效
這種方法有效,因為它遵循了 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,值為:關聯的附件,然后就能獲取當前層級下的節(jié)點內容了,其中”['title'][0]['text']“是排除,如果使用則排除這些節(jié)點,不使用則返回完整的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 = [] # 共用的循環(huán)列表
def get_json_key_value(json_object, key_name, key_value):
"""
獲取相關JSON數據的鍵值進行對比,并返回相關的節(jié)點內容
"""
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返回節(jié)點內容,通過鍵值進行對比,并返回相關的節(jié)點內容,如果未找到則返回空
"""
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 = [] # 執(zhí)行之前清空一下公共數組
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并指定文件名稱內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
pycharm終端無法激活conda環(huán)境的解決辦法
如果您在PyCharm終端中無法激活conda環(huán)境,可能是由于PyCharm沒有正確配置conda解釋器,這篇文章主要給大家介紹了關于pycharm終端無法激活conda環(huán)境的解決辦法,需要的朋友可以參考下2023-09-09

