起因
最近在写一个和图片处理有关系的系统
结果甲方要求批量导入数据和图片
一琢磨,瞬间想到了excel,这玩意儿啥都能放,所以打算从这里上传
结果就遇到了很多问题
这个处理的过程其实很简单,大概是下面的思路
基本思路
- 前端界面:在前端页面中,提供一个上传Excel文件的表单,并在上传成功后,通过Ajax将文件发送到后端Django应用程序中。
- 后端逻辑:在Django应用程序中,编写视图函数来处理上传的Excel文件。可以使用Python库如
pandas
来读取Excel文件,并将数据转换为Django模型实例。- 处理图片:在将Excel中的信息导入到Django模型实例之前,需要将图片从Excel中提取出来,并保存到Django的媒体文件夹中。可以使用Python库如
openpyxl
来处理Excel文件,并使用Django的FileField
字段来保存图片。- 数据库操作:将转换后的Django模型实例保存到数据库中。
- 返回结果:最后,将处理后的结果返回到前端,以便用户可以查看导入的信息和图片
在网上找了一些写法大致是这样的
采用Python openpyxl 提取 Excel 中的图片
前端代码(这个没啥问题)
<!-- 文件上传表单 -->
<form method="post" enctype="multipart/form-data">
{% csrf_token %}
<input type="file" name="file">
<button type="submit">导入</button>
</form>
<!-- 用于显示导入结果的div -->
<div id="result"></div>
<!-- Ajax请求 -->
<script>
$("form").submit(function (event) {
event.preventDefault();
var formData = new FormData($(this)[0]);
$.ajax({
url: "/import-data/",
type: "POST",
data: formData,
processData: false,
contentType: false,
success: function (response) {
$("#result").html(response);
}
});
});
</script>
后端(有一句有大问题,后面细说)
import openpyxl
from django.http import HttpResponse
from django.core.files.storage import default_storage
from django.core.files.base import ContentFile
from django.views.decorators.csrf import csrf_exempt
from .models import MyModel
@csrf_exempt
def import_data(request):
if request.method == "POST":
# 读取上传的文件
file = request.FILES.get("file")
if not file:
return HttpResponse("没有选择文件。")
# 处理Excel文件
wb = openpyxl.load_workbook(file)
sheet = wb.active
for row in sheet.iter_rows(min_row=2):
name = row[0].value
age = row[1].value
picture = row[2].value
# 处理图片
picture_file = default_storage.save("pictures/" + picture.name, ContentFile(picture.read()))
# 创建Django模型实例
MyModel.objects.create(name=name, age=age, picture=picture_file)
return HttpResponse("导入成功。")
else:
return HttpResponse("请求方法不允许。")
经过
没想到困住我的第一件事竟然是
什么才叫做单元格内有图片
离谱吧:一开始点击插入图片直接就出现在单元格里了,但这货能来回拖动,随便一拖动图片就出这个单元格了,经过最后的实验证明;不管图片在哪,只要插入的时候是在单元格内,就算是图片在单元格内了
为什么我会纠结这个呢,
是因为这段代码
image = row[7].value
一直报错:
IndexError: tuple index out of range
所以我就以为是我的单元格内的没有图片。
解决办法
在查找和试过无数次的代码后,无意间发现了这段代码
import openpyxl as opx
wb = opx.load_workbook('Book1.xlsx')
ws = wb.active
ws._images[0] # 第一张图片对象
data = ws._images[0]._data() # 图片的字节数据
# 保存数据为图片
with open("image.png", "wb") as img:
img.write(data)
在本地试了试,结果很顺利的存在了该项目的根目录下
那么问题出在那里呢?
没错,就是这句
data = ws._images[0]._data() # 图片的字节数据
在给出的代码中
image = row[7].value
有一个机制是, openpyxl 是不能读取图片的
在Excel中,图片存储在一个称为“二进制大对象”(Binary Large Object,简称BLOB)的单元格内。这个单元格包含一个指向实际图像文件所在位置的链接,并且还可能包括其他与该图像有关的属性,例如大小和位置等。当用户在Excel中查看或编辑工作表时,Excel会使用这些信息检索并显示相应的图像。
所以我们需要修改代码成这样
image = ws._images[row[0].row-1]._data()
//row[0].row代表这是第几个图片
解决
所以后端修改后的代码时这样的
import openpyxl
from django.http import HttpResponse
from django.core.files.storage import default_storage
from django.core.files.base import ContentFile
from django.views.decorators.csrf import csrf_exempt
from .models import MyModel
@csrf_exempt
def import_data(request):
if request.method == "POST":
# 读取上传的文件
file = request.FILES.get("file")
if not file:
return HttpResponse("没有选择文件。")
# 处理Excel文件
wb = openpyxl.load_workbook(file)
sheet = wb.active
for row in sheet.iter_rows(min_row=2):
name = row[0].value
age = row[1].value
picture= ws._images[row[0].row-1]._data()
# 处理图片
picture_file = default_storage.save("pictures/" + picture.name, ContentFile(picture.read()))
# 创建Django模型实例
MyModel.objects.create(name=name, age=age, picture=picture_file)
return HttpResponse("导入成功。")
else:
return HttpResponse("请求方法不允许。")
切记读有图片的单元格不要用row[1].value,
这句话读出来会显示越界:因为它读不到任何东西