一.项目需求
批量上传图片,然后批量导入(使用excel)每个图片对应的属性(属性共十个,即对应十个字段,其中外键三个)。
二.问题
一次可能上传成百上千张图片和对应字段,原来数据库的设计我将图片和对应属性放在一张表中,图片不可能和对应字段一起批量导入,如果先导入图片,其他字段必须允许为空,且在导入对应属性时,会遍历刚上传已经存在数据库中的图片,然后更新数据库,增加对应属性,这样会给服务器造成很大的压力,且很有可能出现错误(如图片对应不上,因此很多字段为空或只有图片,会使很多错误很难捕捉。)
三.实践中的解决方法
1.分成两张表:
我首先想到将图片和对应字段分开成两张表,先上传图片,然后在导入对应属性,然而仔细一想,问题似乎解决得不完善,导入使如何对应图片id,还是直接对应图片名,还有是否有可能图片已经保存到数据库,但是excel中没有该图片的信息,这也会浪费很多的空间,因此此方法还有待提高。
2.使用缓存:
然后我最后想到了缓存,也决定使用该方法批量上传与导入,思路大概是:上传图片先暂时存入缓存(我这里时图片名为键,图片临时文件对象为值),设置一定的时效,然后在上传excel判断excel的格式及列标题等,这些都对应时,然后将外键数据从数据库取出,一行一行判断excel中的数据的外键是否满足,以及图片是否在缓存中,如果条件都满足,然后这一行数据构成数据库中的一个Queryset对象存入列表,这样就将数据验证完毕,最后验证完所有的数据后,使用bulk_create()方法批量写入,或者可以使用get_or_create()方法批量导入(可以去重,但更耗时)。
2.1图片和excel文件上传序列化如下:
1 class RockImageSerializer(serializers.Serializer): 2 imgs = serializers.ListField(child=serializers.FileField(max_length=100, 3 ), label="地质薄片图片", 4 help_text="地质薄片图片列表", write_only=True) 5 6 def create(self, validated_data): 7 try: 8 imgs = validated_data.get('imgs') 9 notimg_file = [] 10 for img in imgs: 11 img_name = str(img) 12 if not img_name.endswith(('.jpg', '.png', '.bmp', '.JPG', '.PNG', '.BMP')): 13 notimg_file.append(img_name) 14 else: 15 # 将图片加入缓存 16 cache.set(img_name, img, 60 * 60 * 24) 17 if notimg_file: 18 return {'code': -2, 'msg': '部分未上传成功,请检查是否为图片,失败文件部分如下:{0}'.format(','.join(notimg_file[:10]))} 19 return {'code': 1} 20 except Exception as e: 21 return {'code': -1} 22 23 def validate_imgs(self, imgs): 24 if imgs: 25 return imgs 26 else: 27 raise serializers.ValidationError('缺失必要的字段或为空') 28 29 30 class SourceSerializer(serializers.Serializer): 31 """ 32 批量上传序列化(excel) 33 """ 34 source = serializers.FileField(required=True, allow_empty_file=False, 35 error_messages={'empty': '未选择文件', 'required': '未选择文件'}, help_text="excel文件批量导入", 36 label="excel文件")