django+drf+vue上傳excel文件流程梳理
后端實現
1. 后台url.py
urlpatterns = [ ... re_path('^host_excel/', views.HostExcelView.as_view()), ]
2. 后台views.py
from host.utils.read_host_excel import read_host_excel_data class HostExcelView(APIView): # 上傳host數據excel文件 def post(self,request): # request.data中能夠拿到通過formdata上傳的文件和其他鍵值對數據 # request.data>>>> <QueryDict: {'default_password': ['123'], 'host_excel': [<InMemoryUploadedFile: 教學日志模版-吳超-2019年11月份.xlsx (application/vnd.openxmlformats-officedocument.spreadsheetml.sheet)>]}> # from django.core.files.uploadedfile import InMemoryUploadedFile print('request.data>>>>', request.data) host_excel = request.data.get('host_excel') # 后台處理方式1:創建一個excel的臨時存儲目錄(根目錄下創建tem_file),先保存excel文件到本地,然后讀取數據,保存,最后刪除excel文件。 # file_path = f'{settings.BASE_DIR}/tem_file/{host_excel.name}' # with open(file_path, 'wb') as f: # for i in host_excel: # f.write(i) # 針對方式1:如果我們只是為了將數據存儲到數據庫中,為不需要保存excel文件到本地,那么我們需要刪除上傳上來的excel文件 # import os # if os.path.exists(file_path): # 如果文件存在 # # 刪除文件,可使用以下兩種方法。 # os.remove(file_path) # # os.unlink(path) # else: # print('沒有該文件:%s' % file_path) # 則返回文件不存在 # 后台處理方式2: 如果我們只是為了將上傳的excel數據保存到數據庫中,那么我們不需要將excel文件保存到服務器本地,所以我們將接收到的數據,保存到內存bytes數據流中 from io import BytesIO,StringIO sio = BytesIO() for i in host_excel: sio.write(i) #將數據寫入io對象 # 我在這里做了一個excel文件數據讀取的函數,在下面的文件中 # res_data = read_host_excel_data(file_path, default_password) # 方式1:如果我們采用的是后台處理方式1,那么我們傳入excel文件路徑作為參數 res_data = read_host_excel_data(sio, default_password) # 方式2:如果我們采用的是后台處理方式2,那么我們傳入io對象 # 拿到上傳之后的數據之后,我們刪掉上傳上來的臨時excel文件 return Response(res_data)
創建一個py文件存放我們上面處理excel的數據的函數
比如我的在host/utils/read_host_excel.py
def read_host_excel_data(recv_data, default_password=''): # data = xlrd.open_workbook(recv_data) #如果傳入的是文件路徑,那么這樣傳參 data = xlrd.open_workbook(file_contents=recv_data.getvalue()) #如果傳入的是io數據對象,那么別忘了傳參的時候要用關鍵字file_contents=指定一下,下面的處理兩種方式都是一樣的了 # 如下excel的處理,參考我的博客 # 根據索引獲取第一個sheet工作簿 sheet = data.sheet_by_index(0) rows_count = sheet.nrows # print(sheet.name, sheet.nrows, sheet.ncols) # sheet名稱,行數,列數 default_password = default_password # 查詢出所有分類數據 category_list = HostCategory.objects.values_list('id', 'name') # print(category_list) host_info_list = [] for row_number in range(1, rows_count): one_row_dict = {} # print(sheet.cell(row_number, 0)) # 類型:值, 參數:(行號,列號) # print(sheet.cell_type(row_number, 0)) # 單元格數據類型 # print(sheet.cell_value(row_number, 0)) # 單元格的值 category = sheet.cell_value(row_number, 0) # 由於拿到的是分類名稱,所以我們要找到對應名稱的分類id,才能去數據庫里面存儲 for category_data in category_list: # print(category_data[1],type(category_data[1]),category,type(category)) if category_data[1].strip() == category.strip(): one_row_dict['category'] = category_data[0] break # 注意:數據列要對應 one_row_dict['hostname'] = sheet.cell_value(row_number, 1) one_row_dict['ip_addr'] = sheet.cell_value(row_number, 2) one_row_dict['port'] = sheet.cell_value(row_number, 3) one_row_dict['username'] = sheet.cell_value(row_number, 4) # 如果該條記錄中沒有密碼數據,那么使用用戶填寫的默認密碼,如果默認密碼也沒有,那么報錯 # pwd = sheet.cell_value(row_number, 5) print(sheet.cell_value(row_number, 5),type(sheet.cell_value(row_number, 5))) excel_pwd = sheet.cell_value(row_number, 5) try: pwd = str(int(excel_pwd)) # 這樣強轉容易報錯,最好捕獲一下異常,並記錄單元格位置,給用戶保存信息時,可以提示用戶哪個單元格的數據有問題 except: pwd = default_password # 注意:應該提醒用戶,密碼列應該轉換為字符串類型,也就是excel的文本 if not pwd.strip(): pwd = default_password one_row_dict['password'] = pwd one_row_dict['desc'] = sheet.cell_value(row_number, 6) host_info_list.append(one_row_dict) # 校驗主機數據 # 將做好的主機信息字典數據通過我們添加主機時的序列化器進行校驗 res_data = {} # 存放上傳成功之后需要返回的主機數據和某些錯誤信息數據 serializers_host_res_data = [] res_error_data = [] for k, host_data in enumerate(host_info_list): s_obj = HostModelSerializers(data=host_data) # print(s_obj.is_valid()) if s_obj.is_valid(): new_host_obj = s_obj.save() serializers_host_res_data.append(new_host_obj) else: # 報錯,並且錯誤信息中應該體驗錯誤的數據位置 res_error_data.append({'error': f'該{k+1}行數據有誤,其他沒有問題的數據,已經添加成功了,請求失敗數據改完之后,重新上傳這個錯誤數據,成功的數據不需要上傳了'}) # 再次調用序列化器進行數據的序列化,返回給客戶端 s2_obj = HostModelSerializers(instance=serializers_host_res_data, many=True) # print(s2_obj.data) res_data['data'] = s2_obj.data res_data['error'] = res_error_data return res_data
前端vue實現
標簽:<input type="file" id="file">
<button @click="upload">上傳</button>
{
...
data(){
return {}
},
methods:{
upload(){
// 通過axios或者ajax上傳文件,我這里是axios的示例,其實ajax和axios的是差不多的
const formData = new FormData();
// file是通過js獲取的上傳文件對象,不管用什么方式獲取的,只要拿到文件對象就行,我下面舉個例子,針對上面的input標簽
let file_info = document.getElementById('file');
let file = file_info.files[0];
formData.append(`host_excel`, file);
// 通過formdata上傳文件數據,那么axios需要加上如下請求頭鍵值對
this.$axios.post(`${this.$settings.host}/host/host_excel/`, formData, {
headers: {'Content-Type': 'multipart/form-data'},
}).then().catch()
}
}
}
