【问题标题】:Image File upload with Django form using jQuery使用 jQuery 使用 Django 表单上传图像文件
【发布时间】:2014-01-02 00:24:42
【问题描述】:

我正在尝试允许我网站的用户上传图片文件。我想要完成的方法是使用jQuery将文件上传到服务器,如果文件有任何数据(即GeoLocation数据),则解析文件的EXIF数据。如果图像文件没有 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 - 我不再有从文件中获取数据的问题。现在,我只需要在请求附加信息的页面上显示带有获取信息的图像,然后将接收到的图像和信息存储到我的模型中以存储到数据库中。

【问题讨论】:

  • 也请发布您的错误的回溯
  • @sk1p 我已经编辑了帖子以包含回溯

标签: jquery python django


【解决方案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)
# ...

这至少应该纠正您当前遇到的错误。

【讨论】:

  • 进行此更改后,我做了一些测试,上传了一个包含已知 GeoLocation 数据的文件,但 readexif 函数没有找到任何这些数据。我认为这是因为该文件是InMemoryUploadedFile,而exifread.process_file() 方法无法处理该文件类型,但我可能弄错了。想法?
  • 文件内容是否正确传输?您能在浏览器开发工具的网络选项卡中看到它吗?
  • @rwb7041 同样,您可以尝试消除 js 作为错误源 - 尝试在没有任何 javascript 的情况下提交表单并查看它是否有效(确保设置 enctype
  • 对不起,我忘了提到我确实注释掉了 javascript,所以它没有运行。在我的浏览器 (Chrome) 的网络选项卡中,我看到了 POST 请求。当我点击它时,我会在 Request Payload 下看到我上传的图像文件名,内容类型为 image/jpeg
  • 我也继续添加了表单模板(uploadPhoto.html)的内容,仅供参考
猜你喜欢
  • 2012-10-20
  • 2018-01-25
  • 2020-11-04
  • 1970-01-01
  • 1970-01-01
  • 2014-01-12
  • 1970-01-01
  • 1970-01-01
  • 2012-04-25
相关资源
最近更新 更多