I'm trying to allow users of my site to upload image files. The way I want to accomplish this is to use jQuery to upload the file to the server, parse the file's EXIF data if it has any data (namely, GeoLocation data). If the image file does not have the GeoLocation data, I want to ask the user for the data, then show a confirmation page. Otherwise, if I have the data, show the confirmation page with the data. Once the user confirms the data is correct, then the data is added to a Django model. The problems I am currently having are displaying the image on a confirmation page and having the form be submitted via jQuery and not as a usual POST. As you will see in the code, I have methods to get the data out that works when the file path is known, but currently I'm getting the error: coercing to Unicode: need string or buffer, InMemoryUploadedFile found. Here's code for reference (sorry, its a bit long):
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 (utility functions for getting EXIF and other data):
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 (currently this is commented out):
$( "#photoUpload" ).submit(function( event ) { event.preventDefault(); $("#myModalContent").html('Loading...'); // console.log(event); $.post(event.target.action, $( "#photoUpload" ).serialize(), function(data) { $("#myModalContent").html(data); }) }); Any help would be greatly appreciated. UPDATE 1/3 - I'm no longer having the problem of getting the data out of the file. Now, I just need to display the image with the obtained information on a page requesting additional information and then store the image and information received into my model to store into a database.