Google App Engine urlfetch 上传文件
我正在尝试从一个在GAE上的应用程序向torrage.com发送一个文件。这个文件是在用户上传后存储在内存中的。
我想通过这里提供的API来上传这个文件:http://torrage.com/automation.php,但是我在理解如何正确编码这个上传请求的内容时遇到了一些问题,结果收到的消息是“文件为空”。
3 个回答
从这段C代码来看,它使用的是“multipart/form-data”格式,这种格式非常复杂,很容易出错。我不建议像这样手动编写请求的内容。
我用过这个博客里的函数,它在我独立的程序中运行得很好。你可以试试在应用引擎上用用。
http://peerit.blogspot.com/2007/07/multipartposthandler-doesnt-work-for.html
为什么不直接用Python的 urllib2 模块来创建一个POST请求,就像他们在PHP的例子中展示的那样呢?这大概是这样的:
import urrlib, urllib2
data = (
('name', 'torrent'),
('type', 'application/x-bittorrent'),
('file', '/path/to/your/file.torrent'),
)
request = urllib2.urlopen('http://torrage.com/autoupload.php', urllib.urlencode(data))
我觉得torrage的API文档关于POST接口(与SOAP接口相对)写得挺让人困惑的,而且和他们提供的C语言示例代码有些矛盾。在他们的在线PHP示例中,似乎并没有发送文件的内容(就像@kender的回答中也没有发送一样),而在SOAP示例和C语言示例代码中却是发送了文件内容。
C语言示例中相关的部分(他们是如何计算要传给urlfetch.fetch
的头信息的)是:
snprintf(formdata_header, sizeof(formdata_header) - 1,
"Content-Disposition: form-data; name=\"torrent\"; filename=\"%s\"\n"
"Content-Type: " HTTP_UPLOAD_CONTENT_TYPE "\n"
"\n",
torrent_file);
http_content_len = 2 + strlen(content_boundary) + 1 + strlen(formdata_header) + st.st_size + 1 + 2 + strlen(content_boundary) + 3;
LTdebug("http content len %u\n", http_content_len);
snprintf(http_req, sizeof(http_req) - 1,
"POST /%s HTTP/1.1\n"
"Host: %s\n"
"User-Agent: libtorrage/" LTVERSION "\n"
"Connection: close\n"
"Content-Type: multipart/form-data; boundary=%s\n"
"Content-Length: %u\n"
"\n",
cache_uri, cache_host, content_boundary, http_content_len);
"application/x-bittorrent"是HTTP_UPLOAD_CONTENT_TYPE
。st.st_size
是内存缓冲区中所有文件数据的字节数(C语言示例是从文件中读取这些数据,但不管你是怎么把数据放到内存中的,只要你知道它的大小就行)。content_boundary
是一个在文件内容中不存在的字符串,他们把它构建成"---------------------------%u%uLT"
,其中每个%u
都被一个随机数替代(重复这个过程直到生成的字符串在文件中找不到)。最后,在打开HTTP连接并发送其他头信息后,他们这样写POST的主体:
if (write_error == 0) if (write(sock, "--", 2) <= 0) write_error = 1;
if (write_error == 0) if (write(sock, content_boundary, strlen(content_boundary)) <= 0) write_error = 1;
if (write_error == 0) if (write(sock, "\n", 1) <= 0) write_error = 1;
if (write_error == 0) if (write(sock, formdata_header, strlen(formdata_header)) <= 0) write_error = 1;
if (write_error == 0) if (write(sock, filebuf, st.st_size) <= 0) write_error = 1;
if (write_error == 0) if (write(sock, "\n--", 3) <= 0) write_error = 1;
if (write_error == 0) if (write(sock, content_boundary, strlen(content_boundary)) <= 0) write_error = 1;
if (write_error == 0) if (write(sock, "--\n", 3) <= 0) write_error = 1;
这里的filebuf
就是包含文件内容的缓冲区。
这并不是特别简单明了,但我希望这里的信息足够让你找到构建urlfetch.fetch
参数的方法(为urllib.urlopen
构建参数也同样困难,因为问题在于缺乏关于需要什么头信息、内容以及如何编码的文档——而这些不太好找的信息需要从我这里提供的内容中逆向推导出来,我想)。
另外,也许可以通过urlfetch来实现SOAP请求;可以查看这里,了解Carson在这个问题上的尝试、困难和成功的详细帖子。祝你好运!