如何上传CSV,在后台处理它并在Flask中下载新文件

时间:2015-03-16 01:47:00

标签: python flask

我正在建立一个工作的内部工具,只是为了让一些人的生活更轻松。该任务涉及CSV作为输入,它将被处理并生成新文件。该部分已经完成,我制作了一个可行的命令行脚本。但我希望尽可能使其易于访问,因此Web UI是一种自然的选择。我玩了一点烧瓶,用它制作了另一个简单的内部工具,我也想用这个做同样的工具。

我有这个脚本可能需要一段时间来处理文件,有时几分钟。这意味着我希望在Web UI中使其无阻塞。理想情况下,用户选择文件,选择几个选项命中提交,作业在后台启动。应该有一个进度条来向用户显示完成的时间。我不打算在这里进行用户管理,但是任何人都可以查看任何人触发的所有工作的列表。作业完成后,应该有一个链接来下载处理过的文件。

从我读过的内容来看,这是一种任务排队系统与消息代理一起使用的情况,如Celery和Redis。我已经阅读了一些有关此问题的教程,但我仍然不确定该怎么做...从哪里开始?什么是这类问题的最佳架构?另外,如何最好地处理上传部分,也许使用ajax?

如果可能的话,我正在寻找一些建议,参考和代码示例。任何与我想做的事情有关的例子都表示赞赏。

2 个答案:

答案 0 :(得分:5)

根据您发布的内容,您似乎走在了正确的轨道上。您正在努力实现的是将资源需求高的任务卸载到"存根工作者"让您的网络专注于向您的用户展示。

您希望使用的模式通常被称为"发布/订阅"图案:

Pub/Sub

您的申请将发布"发布" Celery任务进入消息代理(Rabbit MQ)。您的Celery任务工作人员将"订阅"到消息代理并查找要执行的任务。非常简单的模式。

考虑到你使用的标签,我会假设你熟悉Flask(伟大的微框架),我建议你看看" Flask-Uploads"为了您的上传需求。

为了处理您的CSV文件,需要创建函数本身并使用@task装饰器将其包装起来,让Celery将其识别为任务。使用了一些" Google-fu",我遇到了这个可能对您有用的特定文件:

https://github.com/captricity/captricity-cloud-io/blob/master/captricity_cloud_io/captricity_cloud_io/tasks.py

查看_upload_to_google函数,了解如何处理此问题。

最后,我最喜欢的作家之一Miguel Grinberg在Celery和Flask上写了一篇很棒的文章,并使用进度条来跟踪你的进度:

http://blog.miguelgrinberg.com/post/using-celery-with-flask

请注意,该示例使用JavaScript每2秒检查一次给定的Celery任务。如果你不期望大量的流量,你当然可以逃脱,但如果容量增长,我建议你看看网络插座。

希望这能让你进入一个良好的起点。祝你好运!

答案 1 :(得分:3)

看来你是解决问题的方法之一

我们需要一个后台工作人员来处理大量数据/文件。如果需要很好的处理时间。 在处理后台任务时,我们没有控制权,所以我们需要DB帮助来跟踪工作状态

按照步骤完成任务,

第1步:在主要路线中执行主要步骤,例如阅读请求(例如:.csv文件)并进行验证,

第2步:创建查找表以跟踪作业状态        此表是占位符,用于跟踪您的后台工作状态

  • 最初它会将状态保存为队列并将作业返回到处理#step5
  • 第3步
  • 返回职位ID

第3步: redis enque:通过redis enque调用处理助手函数并传递所有必需的args,根据需要设置超时值

第4步:进行处理并更新状态记录成功/失败

第5步:创建另一条路线以返回该作业的当前状态,对此路线进行连续的AJAX调用,直到状态变为已完成

通过下面的示例获取更多详细信息假设您已经注意了所有redis设置(例如安装,worke.py,建立redis连接等) #ref

Modles.py (我使用的是Mongo DB)

class TempCsvLookup(Base, Document):
    id = SequenceField(unique=True)
    job_id = StringField()
    file_url = StringField()
    status = StringField()
    created_at = DateTimeField(default=dt.utcnow(), required=True)
    finished_at = DateTimeField(default=(dt.utcnow()  + datetime.timedelta(hours=24)))

<强> views.py

@route('/upload_csv_file', methods=['POST'])
@require_login
def upload_csv_file():

    #step 1
    csv_file = request.form.get('file')
    '''
        do validations
    '''
    #Step 2: create a initial DB record
    look_up = TempCsvLookup(
        status = "Queued"
    ).save()

    #step_3 : calling backgroud task using redis
    job = redis_queue.enqueue(
            process_csv_file,
            args = (
                look_up, csv_file, other_args 
            ),
            timeout = 1200
        )
    job.get_id()

    look_up.update(
        set__job = job.get_id(),
    )

    return jsonfy(job_id = str(job.get_id()))

def process_csv_file(look_up, csv_file, other_args):
    try:
        #step 4 process csv file
        look_up.update(
            set__status = "Processing",
        )

        """ 
           1.do all processing with input csv file
           2.create new csv file
        """
        look_up.update(
            set__status = "Completed",
            set__file_url = new_file_path,
            set__finished_at = dt.utcnow()
        )
    except Exception as e:
        look_up.update(
            set__status = "Failed",
            set__finished_at = dt.utcnow()
        )

@route('/csv_file_lookup/<string:lookup_job_id>', methods=['GET'])
@require_login
def csv_file_lookup(lookup_job_id):
    #step5
    report = TempCsvLookup.objects(job_id=lookup_job_id).first()

    resp = {
        'status': report.status,
        'file_url': report.file_url
    }

    return response.success(data=resp)

希望它可以帮助你