简单的Python Web服务器以保存文件。

17

我正在尝试制作一个简单的Python Web服务器,用于保存通过Post提交到名为store.json的文件中的文本,该文件与Python脚本在同一个文件夹中。这是我的代码的一半,请问剩下的应该是什么?

import string,cgi,time
from os import curdir, sep
from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer
#import pri

class StoreHandler(BaseHTTPRequestHandler):
def do_GET(self):
    try:
        if self.path == "/store.json":
            f = open(curdir + sep + "store.json") #self.path has /test.html
            self.send_response(200)
            self.send_header('Content-type','text/json')
            self.end_headers()
            self.wfile.write(f.read())
            f.close()
            return
        return
    except IOError:
        self.send_error(404,'File Not Found: %s' % self.path)
def do_POST(self):
    //if the url is 'store.json' then
    //what do I do here?

def main():
    try:
        server = HTTPServer(('', 80), StoreHandler)
        print 'started...'
        server.serve_forever()
    except KeyboardInterrupt:
        print '^C received, shutting down server'
        server.socket.close()
if __name__ == '__main__':
    main()

由于API可以并行调用,因此如何避免文件读写的竞态条件? - Mayur
2个回答

35

这是大致的想法:

from os import curdir
from os.path import join as pjoin

from http.server import BaseHTTPRequestHandler, HTTPServer

class StoreHandler(BaseHTTPRequestHandler):
    store_path = pjoin(curdir, 'store.json')

    def do_GET(self):
        if self.path == '/store.json':
            with open(self.store_path) as fh:
                self.send_response(200)
                self.send_header('Content-type', 'text/json')
                self.end_headers()
                self.wfile.write(fh.read().encode())

    def do_POST(self):
        if self.path == '/store.json':
            length = self.headers['content-length']
            data = self.rfile.read(int(length))

            with open(self.store_path, 'w') as fh:
                fh.write(data.decode())

            self.send_response(200)


server = HTTPServer(('', 8080), StoreHandler)
server.serve_forever()
$ curl -X POST --data "one two three four" localhost:8080/store.json
$ curl -X GET localhost:8080/store.json    
one two three four%

2
好的,您需要将 from BaseHTTPServer 更改为 from http.server - Arlen Beiler
1
抱歉,没有注意到标签中的“-3.x”部分。 - gvalkov
1
我发现的另一件事是,在打开命令中必须添加 'rb' 作为参数,否则会出现类型异常。With open(self.store_path,'rb') as fh: - Arlen Beiler
2
好的,我已将有问题的那一行更改为 self.headers['content-length']。我还必须将 postopen 更改为 'wb' - Arlen Beiler
1
@SomethingSomething 是的,完全正确。删除 content type 行,删除 decode()encode(),使用 wbrb 替代 w 和 (nothing)。 - phil294
显示剩余10条评论

11

重要的是,您将需要正确地从原始发布的数据中构建cgi.FieldStorage,例如:

重要的是,您需要正确地从原始发布的数据中构建cgi.FieldStorage,例如:

form = cgi.FieldStorage(
            fp=self.rfile,
            headers=self.headers,
            environ={'REQUEST_METHOD':'POST',
                     'CONTENT_TYPE':self.headers['Content-Type'],
                     })

之后,轻松地卸载文件。以下是一个简单的处理程序,它在do_GET上显示一个表单以上传任何用户选择的文件,并在表单被提交时将该文件保存到/tmp中的do_POST

from BaseHTTPServer import HTTPServer, BaseHTTPRequestHandler

import cgi

class StoreHandler(BaseHTTPRequestHandler):
    def do_POST(self):

        form = cgi.FieldStorage(
            fp=self.rfile,
            headers=self.headers,
            environ={'REQUEST_METHOD':'POST',
                     'CONTENT_TYPE':self.headers['Content-Type'],
                     })
        filename = form['file'].filename
        data = form['file'].file.read()
        open("/tmp/%s"%filename, "wb").write(data)

        self.respond("uploaded %s, thanks"%filename)

    def do_GET(self):
        response = """
        <html><body>
        <form enctype="multipart/form-data" method="post">
        <p>File: <input type="file" name="file"></p>
        <p><input type="submit" value="Upload"></p>
        </form>
        </body></html>
        """        

        self.respond(response)

    def respond(self, response, status=200):
        self.send_response(status)
        self.send_header("Content-type", "text/html")
        self.send_header("Content-length", len(response))
        self.end_headers()
        self.wfile.write(response)  

还需要注意的是,self.respond 不是一个标准方法,我只是添加它以便快速返回一些响应。


这是Python 3,SimpleHTTP还能用吗?抱歉,我之前没提到。 - Arlen Beiler
@ArlenBeiler 我已经改用BaseHTTPRequestHandler,如果在python3上可以工作的话,还有一个SimplreREquestHandler在python3中http://docs.python.org/3.3/library/http.server.html#http.server.SimpleHTTPRequestHandler - Anurag Uniyal
在上传表单后,form['file'].file 为 None。 - Stepan Yakovenko
由于API可以并行调用,因此以这种方式编写文件是否安全? - Mayur

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接