2016-06-20 78 views
2

我有一个应用程序,当前允许用户上传文件并将其保存在Web服务器上。我的客户现在决定使用第三方云托管服务来满足他们的文件存储需求。该公司有自己的API用于在其服务器上执行CRUD操作,因此我编写了一个脚本来测试其API,并将该文件作为base64编码的JSON有效内容发送到API。该脚本工作正常,但现在我坚持如何实现这个功能到Django。通过JSON发送文件,而不是上传到服务器,Django

json_testing.py

import base64 
import json 
import requests 
import magic 

filename = 'test.txt' 

# Open file and read file and encode it as a base64 string 
with open(filename, "rb") as test_file: 
    encoded_string = base64.b64encode(test_file.read()) 

# Get MIME type using magic module 
mime = magic.Magic(mime=True) 
mime_type = mime.from_file(filename) 

# Concatenate MIME type and encoded string with string data 
# Use .decode() on byte data for mime_type and encoded string 
file_string = 'data:%s;base64,%s' % (mime_type.decode(), encoded_string.decode()) 
payload = { 
    "client_id": 1, 
    "file": file_string 
} 
headers = { 
    "token": "AuthTokenGoesHere", 
    "content-type": "application/json", 
} 
request = requests.post('https://api.website.com/api/files/', json=payload, headers=headers) 
print(request.json()) 

models.py

def upload_location(instance, filename): 
    return '%s/documents/%s' % (instance.user.username, filename) 

class Document(models.Model): 
    user = models.ForeignKey(settings.AUTH_USER_MODEL) 
    category = models.ForeignKey(Category, on_delete=models.CASCADE) 
    file = models.FileField(upload_to=upload_location) 

    def __str__(self): 
     return self.filename() 

    def filename(self): 
     return os.path.basename(self.file.name) 

所以要重申的是,当用户上传的某个地方,而不是存储在Web服务器上的文件的文件,我想为base64对文件进行编码,以便我可以将文件作为JSON有效内容发送。任何想法是什么将是最好的方法来解决这个问题?

+0

我不确定我是否理解。您已经知道如何进行适当的请求,问题在哪里? – freakish

+0

@freakish。我只想对文件进行编码,将其作为有效负载发送,然后丢弃文件,而不是将文件保存在Web服务器上。我是否必须上传文件,然后进行编码,然后将其作为JSON有效内容发送,然后删除文件?我想知道是否有某种方式我可以在内存中编码文件,而无需将其保存到Web服务器 – nastyn8

+0

只需从django应用调用u're脚本,而不是用来做什么,我不明白它。 .. –

回答

4

我可以把这个最简单的方法是,我想避免完全保存 文件到Web服务器。我只想编码文件,发送 作为有效载荷,并丢弃它,如果可能的话。

django docs

上传处理程序

当用户上传一个文件,Django的通关文件数据的 上传处理程序 - 它处理文件数据,因为它小班获得 上传。

[ “django.core.files.uploadhandler.MemoryFileUploadHandler”, “django.core.files.uploadhandler.TemporaryFileUploadHandler”]

合:上传处理程序在 FILE_UPLOAD_HANDLERS设置,缺省为最初定义 MemoryFileUploadHandler和TemporaryFileUploadHandler提供了 Django的默认文件上传行为,将小文件读取到 内存和大型磁盘上。

您可以编写定制的Django如何处理文件自定义处理程序。 你可以,例如,使用自定义处理程序来执行用户级 配额,压缩的飞行数据,渲染进度条,甚至发送 到另一个数据存储位置的情况下直接在本地存储See Writing custom upload handlers了解如何您可以定制或完全取代上传行为。

相反的想法:

我想你应该考虑使用默认的文件上传处理坚持,因为他们不断有人上传将压倒服务器的存储器中的文件。

Where uploaded data is stored

保存之前上传的文件,需要将数据存储在某个地方。

默认情况下,如果上传的文件小于2.5兆字节,Django 将在内存中保存上载的全部内容。这意味着 保存文件只涉及从内存读取和写入磁盘 ,因此速度非常快。

但是,如果上传的文件太大,Django会将 上传的文件写入存储在系统临时目录 中的临时文件。在类Unix的平台上,这意味着你可以期望Django生成一个名为/tmp/tmpzfp6I6.upload的文件。如果上传量足够大,您可以观看此文件,因为Django 将数据流式传输到磁盘上。

这些细节 - 2.5兆字节;的/ tmp;等等 - 只是“合理的 默认值”,可以按照下一节中的描述进行定制。


request.FILES info

#forms.py: 

from django import forms 

class UploadFileForm(forms.Form): 
    title = forms.CharField(max_length=50) 
    json_file = forms.FileField() 

处理这种形式将收到的文件数据中request.FILES, 这是包含每个FileField字段的密钥的字典(或 的ImageField的图,或其他FileField子类)。所以上述表格中的数据来自 可以作为request.FILES ['json_file']访问。

注意request.FILES将只包含数据,如果请求方法 是POST和发布请求的<form>具有属性 enctype="multipart/form-data"。否则,request.FILES将为空。


HttpRequest.FILES

包含所有上传的文件的字典状物体。 FILES中的每个键都是<input type="file" name="" />的名称。 FILES中的每个值 都是UploadedFile。


Upload Handlers

当用户上传一个文件,Django的假冒文件数据到 上传处理程序 - 它处理文件数据,因为它得到 上传了一个小班。

[ “django.core.files.uploadhandler.MemoryFileUploadHandler”, “django.core.files.uploadhandler:上载处理程序在 FILE_UPLOAD_HANDLERS设置,默认为最初定义。TemporaryFileUploadHandler“]

source codeTemporaryFileUploadHandler包含此:

lass TemporaryFileUploadHandler(FileUploadHandler): 
    """ 
    Upload handler that streams data into a temporary file. 
    """ 
     ... 
     ... 
     def new_file(self, *args, **kwargs): 
     """ 
     Create the file object to append to as data is coming in. 
     """ 
     ... 
     self.file = TemporaryUploadedFile(....) #<***HERE 

而且source codeTemporaryUploadedFile包含此:

class TemporaryUploadedFile(UploadedFile): 
    """ 
    A file uploaded to a temporary location (i.e. stream-to-disk). 
    """ 
    def __init__(self, name, content_type, size, charset, content_type_extra=None): 
     ... 
     file = tempfile.NamedTemporaryFile(suffix='.upload') #<***HERE 

而且蟒蛇tempfile docs这样说:

tempfile.NamedTemporaryFile(....,删除= TRUE)
...
如果删除为真(默认),该文件一旦它被关闭删除。使用一个内存字节缓冲器

甲流实现:

类似地,其它的两个默认文件上传处理程序,MemoryFileUploadHandler的,创建BytesIO类型的文件。它继承了 BufferedIOBase。当close()方法调用 时,缓冲区将被丢弃。

因此,所有你需要做的就是接近request.FILES[“field_name”]删除的文件(该文件内容是否存储在内存中或在/ tmp文件目录的磁盘),例如:

uploaded_file = request.FILES[“json_file”] 
file_contents = uploaded_file.read() 

#Send file_contents to other server here. 

uploaded_file.close() #erases file 

如果出于某种原因,你不想让django根据目录写入服务器的/tmp目录,那么你需要编写一个自定义文件上传处理程序来拒绝上传的文件太大。

+1

@ nastyn8,我想了解更多,并且默认的文件上传处理程序已经过深思熟虑,所以不需要创建自定义上传处理程序来执行所需操作。请参阅我答案底部的其他解释。 – 7stud

相关问题