使用jQuery使用Django表单上传图像文件

时间:2014-01-02 00:24:43

标签: jquery python django

我正在尝试允许我网站的用户上传图片文件。我想要实现这一点的方法是使用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 - 我不再有从文件中获取数据的问题。现在,我只需要在请求附加信息的页面上显示带有获取信息的图像,然后将收到的图像和信息存储到我的模型中以存储到数据库中。

1 个答案:

答案 0 :(得分:1)

form.cleaned_data['photo']不是文件名,而是InMemoryUploadedFile。小文件by default smaller than 2.5MB甚至没有文件名;它们只存在于记忆中。 InMemoryUploadedFilea 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)
# ...

至少应该纠正你目前所犯的错误。