我正在尝试允许我网站的用户上传图片文件。我想要实现这一点的方法是使用jQuery将文件上传到服务器,解析文件的EXIF数据(如果它有任何数据)(即GeoLocation数据)。如果图像文件没有GeoLocation数据,我想询问用户数据,然后显示确认页面。否则,如果我有数据,请显示包含数据的确认页面。一旦用户确认数据正确,则将数据添加到Django模型中。我目前遇到的问题是在确认页面上显示图像,并通过jQuery提交表单,而不是通常的POST。正如您将在代码中看到的那样,我有获取数据的方法,当文件路径已知时可以正常工作,但是目前我收到错误:强制转换为Unicode:需要字符串或缓冲区,找到InMemoryUploadedFile。这是参考代码(抱歉,它有点长):
forms.py:
from django import forms
from photos.models import PhotoMessages
class PhotoUploadForm(forms.Form):
"""
The form used to upload a user's photo
"""
photo = forms.ImageField(label='Photo to upload', help_text='Must be a valid image', error_messages={'required': 'A photo is required'})
legalAgreement = forms.BooleanField(label='', help_text=PhotoMessages.objects.filter(tag='Legal Agreement')[0].message, error_messages={'required': PhotoMessages.objects.filter(tag='Legal Agreement Error')[0].message})
views.py:
from django.shortcuts import render
from django.http import HttpResponseRedirect
from photos.forms import PhotoUploadForm
from photos.models import PhotoMessages
from photos.utils import readexif, get_client_ip
from datetime import datetime, timedelta
def uploadPhoto(request):
"""
Creates the initial form for uploading a file and handles form errors and proper submission
"""
if request.user.is_authenticated():
if request.method == 'POST': # If the form has been submitted...
form = PhotoUploadForm(request.POST, request.FILES) # A form bound to the POST data
if form.is_valid(): # All validation rules pass
# First, get the image location
image = form.cleaned_data.get('photo')
# TODO the above is a InMemoryUploadedFile
# need to figure out how to use the data in that file, possibly by
# saving its contents to a directory
# then, get the exif data from the file
dat = readexif(image)
# uploaded time
uploaded = datetime.now()
# get the IP address from where the file was uploaded
ip = get_client_ip(request)
# get the user that uploaded the file
user = request.user
# if we have the geolocation data, show the user a confirmation page
if 'lat' in dat:
# we have location info
return render(request, 'confirm.html', {
'img': image,
'lat': dat['lat'],
'lon': dat['lon'],
'taken': dat['taken'],
'uploaded': uploaded,
'ip': ip,
'user': user,
})
# else:
# we need to request the location info
else:
form = PhotoUploadForm() # An unbound form
return render(request, 'uploadPhoto.html', {
'form': form,
})
else:
return render(request, 'notLoggedIn.html', {
'mesg': PhotoMessages.objects.filter(tag='User not logged in')[0].message,
})
utils.py(用于获取EXIF和其他数据的实用程序函数):
import exifread
from datetime import datetime
import dateutil.parser
# DateTime tags for when the image was taken
DT_TAGS = ["Image DateTime", "EXIF DateTimeOriginal", "EXIF DateTimeDigitized", "DateTime"]
def get_client_ip(request):
x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR')
if x_forwarded_for:
ip = x_forwarded_for.split(',')[0]
else:
ip = request.META.get('REMOTE_ADDR')
return ip
def readexif(f):
"""
Method to read in exif data from an image
returns a dictionary of the EXIF data found
"""
ret = {}
dt_value = None
f.open('rb')
ret['processed'] = False
try:
# read the file
tags = exifread.process_file(f)
ret['processed'] = True
# handle time image taken
for dt_tag in DT_TAGS:
try:
dt_value = "%s" % tags[dt_tag]
except:
continue
if dt_value:
ret['taken'] = exif_info2time(dt_value)
# GeoLocation Data
longitude = "%s" % tags['GPS GPSLongitude']
longitude = longitude.strip('[]')
latitude = "%s" % tags['GPS GPSLatitude']
latitude = latitude.strip('[]')
ret['lon'] = deg_min_sec_to_deg(longitude.split(','))
ret['lat'] = deg_min_sec_to_deg(latitude.split(','))
longRef = "%s" % tags['GPS GPSLongitudeRef']
latRef = "%s" % tags['GPS GPSLatitudeRef']
if longRef == 'W':
ret['lon'] = -ret['lon']
if latRef == 'S':
ret['lat'] = -ret['lat']
finally:
f.close()
return ret
def exif_info2time(ts):
return dateutil.parser.parse(ts)
def deg_min_sec_to_deg2(deg, minutes, sec):
return deg + minutes*(1.0/60) + sec * (1.0/3600)
def deg_min_sec_to_deg(arr):
if "/" in arr[0]:
degArr = arr[0].split("/")
deg = float(degArr[0])/float(degArr[1])
else:
deg = float(arr[0])
if "/" in arr[1]:
minArr = arr[1].split("/")
minutes = float(minutesArr[0])/float(minutesArr[1])
else:
minutes = float(arr[1])
if "/" in arr[2]:
secArr = arr[2].split("/")
sec = float(secArr[0])/float(secArr[1])
else:
sec = float(arr[2])
return deg_min_sec_to_deg2(deg, minutes, sec)
upload.js(目前已注释掉):
$( "#photoUpload" ).submit(function( event ) {
event.preventDefault();
$("#myModalContent").html('Loading...');
// console.log(event);
$.post(event.target.action, $( "#photoUpload" ).serialize(), function(data) {
$("#myModalContent").html(data);
})
});
非常感谢任何帮助。 更新1/3 - 我不再有从文件中获取数据的问题。现在,我只需要在请求附加信息的页面上显示带有获取信息的图像,然后将收到的图像和信息存储到我的模型中以存储到数据库中。
答案 0 :(得分:1)
form.cleaned_data['photo']
不是文件名,而是InMemoryUploadedFile
。小文件by default smaller than 2.5MB甚至没有文件名;它们只存在于记忆中。 InMemoryUploadedFile
为a subclass of File
,因此您可以执行以下操作,而不是f = open(fn, 'rb')
:
def readexif(f):
f.open('rb')
tags = exifread.process_file(f)
# ... do more stuff with tags
直接从表单中传递File
,就像您目前正在做的那样:
# ...
dat = readexif(image)
# ...
至少应该纠正你目前所犯的错误。