公司使用的是testlink去维护测试用例,但是因为一些客观原因导致testlink编写测试用例不方便、不易用。同时,testlink的导入只支持xml文件,平常都是大家在excel内编写,然后将excel转为xml,再将xml文件导入到testlink里面。本文只分享上传下载的功能,文件格式内容转换涉及到业务内容就不分享啦。
仅支持上传xls/xlsx文件,如果不是则抛出错误提示
支持拖拽上传或点击上传,一次只能上传单个文件
上传excel后, 右侧展示其所有sheet的checkbox,选中下载
效果演示:
select your Excel here
将文件拖到此处,或点击上传
只能上传xlsx/xls文件,且不超过5M
因为上传接口需要鉴权,所以需要在发送网络请求的时候携带认证信息:
data() {
return {
headers: {
'Authorization': "Token " + this.$auth.token
}
}
},
mounted() {
let backURLl
backURLl = window.VUE_APP_URL.backURL //上传接口
this.backUrl=backURLl + "/api/v1/AvatarUpload/"
},
避免程序出错,需要限制上传的文件类型和大小,在此实现:
export default{
methods: {
// excel表上传
handleBefore(file) {
// 文件类型
console.log(`上传前校验---`, file)
const isType = file.type === 'application/vnd.ms-excel'
const isTypeComputer = file.type === 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
const fileType = isType || isTypeComputer
if (!fileType) {
this.$message.error('上传文件只能是xls/xlsx格式!')
}
// 文件大小限制为5M
const fileLimit = file.size / 1024 / 1024 < 5
if (!fileLimit) {
this.$message.error('上传文件大小不超过5M!')
}
return fileType && fileLimit
},
}
}
常常在使用xlrd模块时,一般都是解析本地的某个实质性、物理的文件。而此处不一样,从前端传送过来,我并不想将其落到磁盘里面,于是直接在内存中直接进行解析文件(InMemoryUploadedFile)
使用方法:
files = request.FILES.get('file')
book = xlrd.open_workbook(filename=None, file_contents=files.read())
整体响应如下:
class AvatarUpload(APIView):
authentication_classes = (TokenAuthentication,) # token认证
permission_classes = (IsAuthenticated,) # IsAuthenticated 仅通过认证的用户
def post(self, request):
'''
:param request:
:return: a list of sheet name
'''
files = request.FILES.get('file')
book = xlrd.open_workbook(filename=None, file_contents=files.read())
sheetnames = book.sheet_names()
filename = files.name
ExcelToXml(book, filename, sheetnames).to_xml()
return Response(sheetnames)
上传文件之后,能够获得其的sheet页,在页面上勾选,点击下载即可。
There is sheets of Excel!
{{city}}
点击下载xml
当勾选或取消勾选选项的时候,需要实时处理数据,保证向后端提供的参数正确:
methods:{
handleCheckedCitiesChange(value) {
this.sheetnames=value
let checkedCount = value.length;
this.isIndeterminate = checkedCount > 0 && checkedCount < this.cities.length;
},
async getXML(){
this.sheetnames.forEach(item=>{
this.$http.getXML(item).then(res=>{
let url = this.backUrl + "?sheetname=" + item
window.open(url)
}).catch(err=>{
this.$message.error("something not found")
})
})
}
在后端下载功能则比较简单了,只需要从指定的目录找到对应的文件,同时在response中设置正确Content-Type、Content-Disposition即可
顺便简单描述一下:
Content-Type:发送端(客户端|服务器)发送的实体的数据类型,常见的text/html,application/json
Content-Disposition:把请求所得的内容存为一个文件的时候提供一个默认的文件名。
实际使用如下:
def get(self, request):
'''
:param request:根据参数确认是下载xml文件 还是 demo模板
:return: a file
'''
BASE_DIR = Path(__file__).resolve().parent.parent
xml_ROOT = os.path.join(BASE_DIR, 'testlink')
sheetname = request.query_params.get("sheetname")
demo = "demo.xlsx"
if sheetname:
sheetname = sheetname + ".xml"
filepath = os.path.join(xml_ROOT, sheetname)
else:
filepath = os.path.join(xml_ROOT, demo)
filename = sheetname if sheetname else demo
if os.path.exists(filepath):
response = FileResponse(open(filepath, 'rb')) # 生成文件对象application/msword application/octet-stream
response['Content-Type'] = 'application/octet-stream'
response['Content-Disposition'] = 'attachment;filename ="%s"' % (
filename.encode('utf-8').decode('ISO-8859-1'))
return response
else:
return Response({"notFound": sheetname}, status=400)