生成大文件并发送

时间:2015-06-25 12:11:35

标签: python tornado large-files

我有一个相当大的.csv文件(最多100万行),我想在浏览器请求时生成和发送。

我当前的代码是(除了我实际上没有生成相同的数据):

class CSVHandler(tornado.web.RequestHandler): 
  def get(self):
    self.set_header('Content-Type','text/csv')
    self.set_header('content-Disposition','attachement; filename=dump.csv')  
    self.write('lineNumber,measure\r\n') # File header
    for line in range(0,1000000): 
      self.write(','.join([str(line),random.random()])+'\r\n') # mock data

app = tornado.web.Application([(r"/csv",csvHandler)])
app.listen(8080)

我对上述方法的问题是:

  • Web浏览器不直接开始下载发送的块。它挂起,而网络服务器似乎准备整个内容。
  • Web服务器在处理此请求时被阻止,并使其他客户端挂起。

2 个答案:

答案 0 :(得分:8)

默认情况下,所有数据都在内存中缓冲,直到请求结束,以便在发生异常时将其替换为错误页面。要以递增方式发送响应,您的处理程序必须是异步的(因此它可以与响应的写入和IOLoop上的其他请求交错)并使用RequestHandler.flush()方法。

请注意,“异步”与“使用@tornado.web.asynchronous装饰器”不同;在这种情况下,我建议使用@tornado.gen.coroutine代替@asynchronous。这允许您在每次刷新时简单地使用yield运算符:

class CSVHandler(tornado.web.RequestHandler): 
    @tornado.gen.coroutine
    def get(self):
        self.set_header('Content-Type','text/csv')
        self.set_header('content-Disposition','attachment; filename=dump.csv')  
        self.write('lineNumber,measure\r\n') # File header
        for line in range(0,1000000): 
            self.write(','.join([str(line),random.random()])+'\r\n') # mock data
            yield self.flush()

self.flush()启动将数据写入网络的过程,yield等待数据到达内核。这使得其他处理程序可以运行并且还可以帮助管理内存消耗(通过限制客户端下载速度可以提前多远)。在CSV文件的每一行之后刷新有点贵,因此您可能只想在每100或1000行之后刷新。

请注意,如果下载开始后出现异常,则无法向客户端显示错误页面;你只能在中途减少下载。尝试验证请求并在第一次调用flush()之前执行可能失败的所有操作。

答案 1 :(得分:1)

  • 对于第一个问题,您需要flush()给定输出缓冲区的块。

    来自documentation(强调重点):

      

    RequestHandler.write(chunk)[source]

         

    将给定的块写入输出缓冲区。

         

    要将输出写入网络,请使用下面的flush()方法。

  • 关于您的应用程序挂起,您正在从主线程提供请求,因此所有内容都将等待您的操作完成。您应该使用Tornado的iostream进行此操作。来自tornado.iostream documentation

      

    tornado.iostream - 非阻塞套接字的便捷包装器   用于写入和读取非阻塞文件和套接字的实用程序类。