Python,App Engine:HTTP多部分POST到Vk.Ads API [英] Python, App Engine: HTTP multipart POST to Vk.Ads API

查看:112
本文介绍了Python,App Engine:HTTP多部分POST到Vk.Ads API的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我花了几天的时间尝试搜索示例和文档,也在ruSO上没有解决.因此,我希望可以在enSO上找到解决方案.

I tried to sole this problem myself for a few days searching on examples and documentations, also it wasn't solved on ruSO. So, I hope that solution will be found here, on enSO.

我开发了一项服务,可使用Python和Google App Engine在Vk社交网络上自动创建广告.最初,用于广告的图片会加载到我的服务器上(第1部分),然后有时将它们上传到Vk服务器上(第2.1部分和第2.2 部分).似乎图片已正确加载并存储在我的服务器上(我下载了图片并与原始图片进行了比较-每个字节都是相同的).但是我附上第1部分代码,以防万一.

I develop a service for automatic creation of ads at Vk social network using Python and Google App Engine. Initially, pictures for ads are loaded to my server (part 1), then they are uploaded to Vk server at some time (parts 2.1 and 2.2). It seems that pictures are loaded and stored on my server correctly (I downloaded them and compared with original ones — every byte is the same). But I attach the part 1 code just in case.

首先要将图片上传到Vk.Ads,我需要才能获取URL -这很简单,因此请跳过它.其次,我需要向带有字段file的链接发送POST请求,其中包含照片的二进制内容(

To upload a picture to Vk.Ads firstly I need to get a URL — this is simple, so skip it. Secondly, I need to send a POST request to this link with field file with binary content of the photo (API documentation). I created two ways for that (2.1 and 2.2), but both of them returns errcode: 2 which means corrupted file. To my mind, the problem is about the requests, but I don't exclude the possibility of it files uploading/storage on my server, or some strange work of the API. I'll appreciate any answers and comments.

import webapp2
from google.appengine.ext import ndb

# stores pictures on the server
class Photo(ndb.Model):
    name = ndb.StringProperty()
    img = ndb.BlobProperty()

    @staticmethod
    def get(name):
        retval = Photo.query(Photo.name == name).get()
        return retval

    @staticmethod
    def create(name, blob):
        retval = Photo()
        retval.name = name
        retval.img = blob
        return retval

class PhotosPage(webapp2.RequestHandler):
    def get(self):
        # general content of the page:
        html = '''<form action="/photos" method="post" enctype="multipart/form-data">
            <input type="file" name="flimg"/>
            <input value="new_pic" name="flname"/>
            <input type="submit" value="Upload"/> </form>'''

    def post(self):
        n = str(self.request.get('flname'))
        f = self.request.get('flimg')
        p = Photo.get(n)
        if p:
            p.img = f
        else:
            p = Photo.create(n, f)
        p.put()

2.1.使用 urlfetch POST到API,方法#1 и海报:

2.1. POST to API, approach #1, using urlfetch и poster:

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

name = 'file'
content = ... # file binary content
where = ... # gotten URL

options = {
    'file': MultipartParam(
        name=name,
        value=content,
        filename=name,
        filetype='image/png',
        filesize=len(content))
}

data, headers = multipart_encode(options)
pocket = "".join(data)

result = urlfetch.fetch(
    url=where,
    payload=pocket,
    method=urlfetch.POST,
    headers=headers)

2.2.使用requests:

POST到API,方法2

2.2. POST to API, approach #2, using requests:

import requests

name = 'file'
content = ... # file binary content
where = ... # gotten URL

# I also tried without this dict; is it necessary?
data = {
    'fileName': name,
    'fileSize': len(content),
    'description': 'undefined',
}

result = requests.post(where, files={name: StringIO(content)}, data=data)

此外,对于第二种方法,我提取了请求的内容:

In addition, for the second approach I extracted the content of my request:

POST
https://pu.vk.com/c.../upload.php?act=ads_add&mid=...&size=m&rdsn=1&hash_time=...&hash=...&rhash=...&api=1

Content-Length: 15946
Content-Type: multipart/form-data; boundary=b4b260eace4e4a7082a99753b74cf51f

--b4b260eace4e4a7082a99753b74cf51f
Content-Disposition: form-data; name="description"
undefined

--b4b260eace4e4a7082a99753b74cf51f
Content-Disposition: form-data; name="fileSize"
15518

--b4b260eace4e4a7082a99753b74cf51f
Content-Disposition: form-data; name="fileName"
file

--b4b260eace4e4a7082a99753b74cf51f
Content-Disposition: form-data; name="file"; filename="file" 
< File binary content >

--b4b260eace4e4a7082a99753b74cf51f-- 


更新.

由于 SwiftStudier ,我发现了问题的根源:StringIOBytesIO与文件open的行为不同.如果仅使用open,则代码可以很好地工作,但不适用于虚拟文件.如何解决?


UPDATE.

Thanks to SwiftStudier, I found the origin of the problem: StringIO and BytesIO don't behave identical to file open. If I use just open the code works well, but it doesn't with virtual file. How it can be solved?

import requests
from io import BytesIO

with open('path.to/file.png', 'rb') as fin:
    content = BytesIO(fin.read())

token = '...'
url = 'https://api.vk.com/method/ads.getUploadURL?access_token=' + token + '&ad_format=2'
upload_url = requests.get(url).json()['response']

post_fields = {
    'access_token': token
}

data_fields = {
    # This works:
    # 'file': open('path.to/file.png', 'rb')

    # But this does not:
    'file': content
}

response = requests.post(upload_url, data=post_fields, files=data_fields)
print(response.text)

推荐答案

经过大量实验并研究了不同的HTTP请求内容,我发现了错误代码与有效代码之间的唯一区别.它只有大约4个字节:文件名必须包含扩展名. Vk API甚至会忽略Content-Type: image/png,但需要.png或类似的文件名.因此,这不起作用:

After a lot of experiments and investigating of different HTTP requests content I found out the only difference between wrong and working code. It was about 4 bytes only: file name MUST contain the extension. Vk API even ignores Content-Type: image/png, but needs .png or similar in filename. So, this doesn't work:

requests.post(upload_url, files={
    'file': BytesIO('<binary file content>')
})

但是此选项可以正常工作:

But this option works properly:

requests.post(upload_url, files={
    'file': ('file.png', BytesIO('<binary file content>'), 'image/png')
})

就像这样,它不适用于GAE:

Just like this one, which is not available for GAE:

requests.post(upload_url, files={
    'file': open('/path/to/image.png', 'rb')
})


StringIOStringIO都适合该任务.如前所述,Content-Type没关系,可以只是multipart/form-data.


Both StringIO and StringIO are appropriate for that task. As mentioned, Content-Type does not matter, it can be just multipart/form-data.

这篇关于Python,App Engine:HTTP多部分POST到Vk.Ads API的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆