javascript 创建一个执行 python 脚本的超链接(或按钮),然后在脚本完成时重定向
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/10903615/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Create a hyperlink (or button) that executes a python script and then redirects when script completes
提问by bem3ry
Alright, I managed to get it working how I want (although some/most might disagree with the method). I used Flask as recommended below. The part that might be considered "wrong" is the 404 check loop. It isn't proper design and results in a "stuck script" error in some browsers if it goes on for too long. However, the script I want to run doesn't last long enough for that to be a problem.
好吧,我设法让它按照我想要的方式工作(尽管有些/大多数人可能不同意该方法)。我按照下面的推荐使用了 Flask。可能被认为“错误”的部分是 404 检查循环。这不是正确的设计,如果持续时间过长,会在某些浏览器中导致“卡住脚本”错误。但是,我要运行的脚本持续时间不够长,因此不会成为问题。
Thanks for the help and please let me know if you have any other suggestions.
感谢您的帮助,如果您有任何其他建议,请告诉我。
Flask App:
烧瓶应用程序:
import threading
import subprocess
import os
import sys
from flask import Flask
from flask import render_template, abort
app = Flask(__name__)
app.debug = True
def run_script():
theproc = subprocess.Popen([sys.executable, "run_me.py"])
theproc.communicate()
@app.route('/')
def index():
return render_template('index.html')
@app.route('/generate')
def generate():
threading.Thread(target=lambda: run_script()).start()
return render_template('processing.html')
@app.route('/is_done')
def is_done():
hfile = "templates\itworked.html"
if os.path.isfile(hfile):
return render_template('itworked.html')
else:
abort(404)
if __name__ == "__main__":
app.run()
processing.html:
处理.html:
<!DOCTYPE html>
<html>
<head>
<script src="/static/jquery.min.js"></script>
</head>
<body>
<p>Processing...</p>
<script>
setInterval(function()
{
var http = new XMLHttpRequest();
http.open('HEAD', "is_done", false);
http.send();
if (http.status==200)
window.location.replace("is_done");
},2000);
</script>
</body>
</html>
Original question:
原问题:
I am a beginner/intermediate Python programmer and a beginning web developer so please be gentle. What I want: A user clicks a hyperlink and is taken to an intermediate web page that says something like "Processing...". In the background (on the server) this link triggers a python script that processes a file and creates a new web page. On completion of the script, the user is redirected to the newly created page. What I have: I have a script that does the processing and spits out a new page. What I haven't figured out is how to trigger the python script and then trigger the redirect on script completion. I've looked at web frameworks like django, and cgi scripting. But I haven't found anything I feel fits the bill. It's very possible I'm just missing something completely obvious so I'd really appreciate any help. Thanks.
我是初学者/中级 Python 程序员和初级 Web 开发人员,所以请保持温和。我想要什么:用户单击一个超链接并被带到一个中间网页,上面写着“正在处理...”之类的东西。在后台(在服务器上),此链接会触发一个 Python 脚本,该脚本处理一个文件并创建一个新的网页。脚本完成后,用户将被重定向到新创建的页面。我所拥有的:我有一个脚本可以进行处理并吐出一个新页面。我还没有弄清楚如何触发 python 脚本,然后在脚本完成时触发重定向。我看过像 django 和 cgi 脚本这样的网络框架。但我还没有找到任何我觉得符合要求的东西。我很可能只是遗漏了一些完全明显的东西,所以我真的很感激任何帮助。
采纳答案by mensi
A simple way to tackle this problem would be to use a simple web framework like Flask to build the web part uf your system. In the request handler for your magic link, you would need to spawn your script and keep track of it. A simple way to see if your script is done and relay that to your user is to periodically send off an ajax request to check for completion.
解决这个问题的一种简单方法是使用像 Flask 这样的简单 Web 框架来构建系统的 Web 部件。在魔术链接的请求处理程序中,您需要生成脚本并对其进行跟踪。查看脚本是否完成并将其转发给用户的一种简单方法是定期发送 ajax 请求以检查是否完成。
So for example the Flask website could look like:
因此,例如 Flask 网站可能如下所示:
import threading
import subprocess
import uuid
from flask import Flask
from flask import render_template, url_for, abort, jsonify, request
app = Flask(__name__)
background_scripts = {}
def run_script(id):
subprocess.call(["/path/to/yourscript.py", "argument1", "argument2"])
background_scripts[id] = True
@app.route('/')
def index():
return render_template('index.html')
@app.route('/generate')
def generate():
id = str(uuid.uuid4())
background_scripts[id] = False
threading.Thread(target=lambda: run_script(id)).start()
return render_template('processing.html', id=id)
@app.route('/is_done')
def is_done():
id = request.args.get('id', None)
if id not in background_scripts:
abort(404)
return jsonify(done=background_scripts[id])
And the index.html
:
和index.html
:
<a href="{{ url_for('generate') }}">click me</a>
And processing.html
:
并且processing.html
:
<html>
<head>
<script src="/static/jquery.js"></script>
<script>
function ajaxCallback(data) {
if (data.done)
window.location.replace("http://YOUR_GENERATED_PAGE_URL");
else
window.setTimeout(function() {
$.getJSON('{{ url_for('is_done') }}', {id: {{ id }} }, ajaxCallback);
}, 3000);
}
$(document).ready(function(){
ajaxCallback({done=false});
});
</script>
</head>
<body>
Processing...
</body></html>
This is all untested code at the moment, but I hope you get some idea on how to approach this problem. Also keep in mind that this will only work if you serve the page from one process, so if you set up Apache and mod_wsgi, make sure there is only one process in the process group.
目前这些都是未经测试的代码,但我希望您对如何解决这个问题有所了解。还要记住,这仅在您从一个进程提供页面时才有效,因此如果您设置了 Apache 和 mod_wsgi,请确保进程组中只有一个进程。
If you need a more complex solution, you might want to look at message queues and the like.
如果您需要更复杂的解决方案,您可能需要查看消息队列等。
回答by Andrew Gorcester
This is probably too heavy-duty a solution for you, but assuming you cannot do away with the "creating a file on the server" part, this is considered a "best practice" solution in web development:
这对您来说可能是一个过于繁重的解决方案,但假设您无法取消“在服务器上创建文件”部分,这被认为是 Web 开发中的“最佳实践”解决方案:
Use a web framework like Flask or Django to serve pages. When the user requests the job be started via a request (preferably a POST request because GET requests should not cause significant side-effects), the framework sends a message to a delayed task system like Celery. The user is then shown a page saying that the job has been submitted.
使用诸如 Flask 或 Django 之类的 Web 框架来提供页面。当用户通过请求(最好是 POST 请求,因为 GET 请求不应该引起显着的副作用)请求启动作业时,框架会向像 Celery 这样的延迟任务系统发送一条消息。然后向用户显示一个页面,说明作业已提交。
At this point you can use ajax to poll the server and see when the job is completed, but typically you don't want that to be your only method of seeing if the job is completed. If the connection is interrupted for any reason, for instance if the user mistakenly closes the browser or the tab (very common), the effort will be wasted and the user will have to start the process over again. You should have a backup way of communicating with the user if at all possible; this could be a flash message that appears elsewhere on the site, an automated "job finished" email, a box in the homepage that shows recently completed jobs, etc. How practical and important this is depends on whether the user is logged in and how long the job takes/whether it has side effects other than the creation of a file.
此时,您可以使用 ajax 轮询服务器并查看作业何时完成,但通常您不希望这是查看作业是否完成的唯一方法。如果连接因任何原因中断,例如,如果用户错误地关闭了浏览器或选项卡(非常常见),则工作将白费,用户将不得不重新开始该过程。如果可能的话,你应该有一个与用户沟通的备用方式;这可能是出现在网站其他地方的快速消息、自动“作业完成”电子邮件、主页中显示最近完成的作业的框等。这有多实用和重要取决于用户是否登录以及如何登录工作需要多长时间/是否有除创建文件之外的副作用。
Finally you can allow the user to access the page through a url unique to that user's job results. Be sure to inform the user if that url will not be valid indefinitely, for instance if the file will be deleted on a timer.
最后,您可以允许用户通过该用户的作业结果唯一的 url 访问该页面。如果该 url 不会无限期地有效,请务必通知用户,例如,如果该文件将在计时器上被删除。
回答by Koobz
Two simplish ways to do this:
两种简单的方法来做到这一点:
1) Use ajax to poll the server. Check for completion every 10 seconds or whatever interval you deem appropriate. E.g. while the script is processing, it writes to a file, or database, whatever it might be to indicate the completion percentage as well as identify the process somehow (in case you have multiple scripts running concurrently you'll want to know which one is Bob's and which one is Suzy's).
1)使用ajax轮询服务器。每 10 秒或您认为合适的任何时间间隔检查一次是否完成。例如,当脚本正在处理时,它会写入一个文件或数据库,无论它以何种方式指示完成百分比以及以某种方式识别进程(如果您有多个脚本同时运行,您会想知道哪个是鲍勃的,哪一个是苏西的)。
2) Disable output buffering and stream the output to the client. This is simpler than the first option. Basically, run the script and as it's processing you can output javascript code to say, update a progress indicator. You need to disable output buffering, or manually flush the buffer otherwise your client might not receive the output immediately (the indicator update may only be visible to the client when it hits 100%). Google how to do it on whatever setup (e.g. PHP, Django) you're running.
2) 禁用输出缓冲并将输出流式传输到客户端。这比第一个选项更简单。基本上,运行脚本,在处理过程中,您可以输出 javascript 代码来更新进度指示器。您需要禁用输出缓冲,或手动刷新缓冲区,否则您的客户端可能不会立即收到输出(指标更新可能只有在达到 100% 时才对客户端可见)。谷歌如何在您运行的任何设置(例如 PHP、Django)上执行此操作。
回答by jatrim
What you want is possible, but it's really about 3 problems.
你想要的是可能的,但这实际上是关于 3 个问题。
The first question is, how do you create a new file from PHP. I can't really answer this one. What you need to do, probably depends on what kind of file you're trying to create and why.
第一个问题是,如何从 PHP 创建一个新文件。这个我真的答不上来。您需要做什么,可能取决于您要创建的文件类型以及原因。
The second question is, how do you let the user know this is happening, and the third is how do you redirect to the new page.
第二个问题是,你如何让用户知道这正在发生,第三个问题是你如何重定向到新页面。
Personally, I think the shortest route to victory, is probably to have your client-side javascript make an AJAX request to the server page or CGI module that creates your new content.
就个人而言,我认为通往胜利的最短途径可能是让您的客户端 javascript 向创建新内容的服务器页面或 CGI 模块发出 AJAX 请求。
- On button click, make an ajax request to the PHP page that generates your new content page.
- Present your busy indicator.
- Have the requested page return the URL of the new page as its content.
- When the request completes redirect to the returned URL via javascript.
- 单击按钮时,向生成新内容页面的 PHP 页面发出 ajax 请求。
- 展示您的忙碌指示符。
- 让请求的页面返回新页面的 URL 作为其内容。
- 当请求完成时,通过 javascript 重定向到返回的 URL。
As someone new to web development, the easiest thing to do might be to look into jQuery and it's AJAX functionality. The wrappers there make it pretty easy to do the above. Of course you can do the same with any of the other major javascript frameworks or straight javascript as well.
作为 Web 开发的新手,最简单的方法可能是研究 jQuery 及其 AJAX 功能。那里的包装器使执行上述操作变得非常容易。当然,您也可以对任何其他主要 javascript 框架或直接 javascript 执行相同操作。
For reference:
以供参考: