在appengine python中使用multipart / form-data发布请求不起作用

时间:2022-11-23 17:46:36

I'm attempting to send a multipart post request from an appengine app to an external (django) api hosted on dotcloud. The request includes some text and a file (pdf) and is sent using the following code

我正在尝试从appengine应用程序向dotcloud上托管的外部(django)api发送多部分帖子请求。该请求包括一些文本和文件(pdf),并使用以下代码发送

from google.appengine.api import urlfetch
from poster.encode import multipart_encode
from libs.poster.streaminghttp import register_openers

register_openers()
file_data = self.request.POST['file_to_upload']
the_file = file_data
send_url = "http://127.0.0.1:8000/"
values = {
          'user_id' : '12341234',
          'the_file' : the_file
          }

data, headers = multipart_encode(values)
headers['User-Agent'] = 'Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)'
data = str().join(data)
result = urlfetch.fetch(url=send_url, payload=data, method=urlfetch.POST, headers=headers)
logging.info(result.content)

When this method runs Appengine gives the following warning (I'm not sure if it's related to my issue)

当此方法运行时,Appengine会发出以下警告(我不确定它是否与我的问题有关)

Stripped prohibited headers from URLFetch request: ['Content-Length']

And Django sends through the following error

并且Django通过以下错误发送

<class 'django.utils.datastructures.MultiValueDictKeyError'>"Key 'the_file' not found in <MultiValueDict: {}>"

The django code is pretty simple and works when I use the postman chrome extension to send a file.

django代码非常简单,当我使用postman chrome扩展程序发送文件时可以使用。

@csrf_exempt
def index(request):
    try:
        user_id = request.POST["user_id"]
        the_file = request.FILES["the_file"]
        return HttpResponse("OK")
    except:
        return HttpResponse(sys.exc_info())

If I add

如果我加

print request.POST.keys()

I get a dictionary containing user_id and the_file indicating that the file is not being sent as a file. if I do the same for FILES i.e.

我得到一个包含user_id和the_file的字典,表明该文件没有作为文件发送。如果我为FILES做同样的事情,即

print request.FILES.keys()    

I get en empty list [].

我得到空列表[]。

EDIT 1:

I've changed my question to implement the suggestion of someone1 however this still fails. I also included the headers addition recommended by the link Glenn sent, but no joy.

我已经改变了我的问题来实现某人的建议1然而这仍然失败了。我还包括了Glenn发送链接推荐的标题,但没有任何乐趣。

EDIT 2:

I've also tried sending the_file as variations of

我也试过发送the_file作为变种

the_file = file_data.file
the_file = file_data.file.read()

But I get the same error.

但我得到了同样的错误。

EDIT 3:

I've also tried editing my django app to

我也尝试过编辑我的django应用程序

the_file = request.POST["the_file"]

However when I try to save the file locally with

但是当我尝试在本地保存文件时

path = default_storage.save(file_location, ContentFile(the_file.read()))

it fails with

它失败了

<type 'exceptions.AttributeError'>'unicode' object has no attribute 'read'<traceback object at 0x101f10098>

similarly if I try access the_file.file (as I can access in my appengine app) it tells me

同样,如果我尝试访问the_file.file(因为我可以在我的appengine应用程序中访问),它告诉我

<type 'exceptions.AttributeError'>'unicode' object has no attribute 'file'<traceback object at 0x101f06d40>

2 个解决方案

#1


9  

Here is some code I tested locally that should do the trick (I used a different handler than webapp2 but tried to modify it to webapp2. You'll also need the poster lib found here http://atlee.ca/software/poster/):

这是我在本地测试的一些代码应该做的伎俩(我使用了与webapp2不同的处理程序,但试图将其修改为webapp2。你还需要在这里找到的海报库http://atlee.ca/software/poster/ ):

In your POST handler on GAE:

在GAE的POST处理程序中:

from google.appengine.api import urlfetch
from poster.encode import multipart_encode
payload = {}
payload['test_file'] = self.request.POST['test_file']
payload['user_id'] = self.request.POST['user_id']
to_post = multipart_encode(payload)
send_url = "http://127.0.0.1:8000/"
result = urlfetch.fetch(url=send_url, payload="".join(to_post[0]), method=urlfetch.POST, headers=to_post[1])
logging.info(result.content)

Make sure your HTML form contains method="POST" enctype="multipart/form-data". Hope this helps!

确保您的HTML表单包含method =“POST”enctype =“multipart / form-data”。希望这可以帮助!

EDIT: I tried using the webapp2 handler and realized the way files are served are different than how the framework I used to test with works (KAY). Here is updated code that should do the trick (tested on production):

编辑:我尝试使用webapp2处理程序,并意识到文件的服务方式不同于我用来测试工作的框架(KAY)。这是更新的代码应该做的技巧(在生产测试):

import webapp2
from google.appengine.api import urlfetch
from poster.encode import multipart_encode, MultipartParam

class UploadTest(webapp2.RequestHandler):
  def post(self): 
    payload = {}
    file_data = self.request.POST['test_file']
    payload['test_file'] = MultipartParam('test_file', filename=file_data.filename,
                                          filetype=file_data.type,
                                          fileobj=file_data.file)
    payload['name'] = self.request.POST['name']
    data,headers= multipart_encode(payload)
    send_url = "http://127.0.0.1:8000/"
    t = urlfetch.fetch(url=send_url, payload="".join(data), method=urlfetch.POST, headers=headers)
    self.response.headers['Content-Type'] = 'text/plain'
    self.response.out.write(t.content)
  def get(self):
    self.response.out.write("""
    <html>
        <head>
            <title>File Upload Test</title>
        </head>
        <body>
            <form action="" method="POST" enctype="multipart/form-data">
                <input type="text" name="name" />
                <input type="file" name="test_file" />
                <input type="submit" value="Submit" />
            </form>
        </body>
    </html>""")

#2


2  

You're urlencoding the data where you should be multipart_encoding it. Have a look at this: Trying to post multipart form data in python, won't post

你正在对你应该对其进行multipart_encoding的数据进行urlencoding。看看这个:尝试在python中发布多部分表单数据,不会发布

#1


9  

Here is some code I tested locally that should do the trick (I used a different handler than webapp2 but tried to modify it to webapp2. You'll also need the poster lib found here http://atlee.ca/software/poster/):

这是我在本地测试的一些代码应该做的伎俩(我使用了与webapp2不同的处理程序,但试图将其修改为webapp2。你还需要在这里找到的海报库http://atlee.ca/software/poster/ ):

In your POST handler on GAE:

在GAE的POST处理程序中:

from google.appengine.api import urlfetch
from poster.encode import multipart_encode
payload = {}
payload['test_file'] = self.request.POST['test_file']
payload['user_id'] = self.request.POST['user_id']
to_post = multipart_encode(payload)
send_url = "http://127.0.0.1:8000/"
result = urlfetch.fetch(url=send_url, payload="".join(to_post[0]), method=urlfetch.POST, headers=to_post[1])
logging.info(result.content)

Make sure your HTML form contains method="POST" enctype="multipart/form-data". Hope this helps!

确保您的HTML表单包含method =“POST”enctype =“multipart / form-data”。希望这可以帮助!

EDIT: I tried using the webapp2 handler and realized the way files are served are different than how the framework I used to test with works (KAY). Here is updated code that should do the trick (tested on production):

编辑:我尝试使用webapp2处理程序,并意识到文件的服务方式不同于我用来测试工作的框架(KAY)。这是更新的代码应该做的技巧(在生产测试):

import webapp2
from google.appengine.api import urlfetch
from poster.encode import multipart_encode, MultipartParam

class UploadTest(webapp2.RequestHandler):
  def post(self): 
    payload = {}
    file_data = self.request.POST['test_file']
    payload['test_file'] = MultipartParam('test_file', filename=file_data.filename,
                                          filetype=file_data.type,
                                          fileobj=file_data.file)
    payload['name'] = self.request.POST['name']
    data,headers= multipart_encode(payload)
    send_url = "http://127.0.0.1:8000/"
    t = urlfetch.fetch(url=send_url, payload="".join(data), method=urlfetch.POST, headers=headers)
    self.response.headers['Content-Type'] = 'text/plain'
    self.response.out.write(t.content)
  def get(self):
    self.response.out.write("""
    <html>
        <head>
            <title>File Upload Test</title>
        </head>
        <body>
            <form action="" method="POST" enctype="multipart/form-data">
                <input type="text" name="name" />
                <input type="file" name="test_file" />
                <input type="submit" value="Submit" />
            </form>
        </body>
    </html>""")

#2


2  

You're urlencoding the data where you should be multipart_encoding it. Have a look at this: Trying to post multipart form data in python, won't post

你正在对你应该对其进行multipart_encoding的数据进行urlencoding。看看这个:尝试在python中发布多部分表单数据,不会发布