我花了几天的时间尝试搜索示例和文档,也在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请求,其中包含照片的二进制内容( API文档).我为此创建了两种方法( 2.1和2.2 ),但是它们都返回errcode: 2,这意味着corrupted file.在我看来,问题与请求有关,但我不排除文件在服务器上上载/存储或API的某些奇怪工作的可能性.我将不胜感激任何答复和评论.
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 pu.vk/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 ,我发现了问题的根源:StringIO和BytesIO与文件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 = 'api.vk/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') })
StringIO和StringIO都适合该任务.如前所述,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
发布评论