Linux 下curl模拟Http 的get or post请求

本文详细介绍了curl命令的基本使用,包括GET和POST请求的使用方法,以及各种常用参数的解释和示例。通过实例演示,帮助读者掌握curl命令在实际开发中的应用。

一、get请求


curl "http://www.baidu.com"  如果这里的URL指向的是一个文件或者一幅图都可以直接下载到本地

curl -i "http://www.baidu.com"  显示全部信息

curl -l "http://www.baidu.com" 只显示头部信息

curl -v "http://www.baidu.com" 显示get请求全过程解析


wget "http://www.baidu.com"也可以


二、post请求

curl -d "param1=value1&param2=value2" "http://www.baidu.com"




用途说明

curl命令是一个功能强大的网络工具,它能够通过http、ftp等方式下载文件,也能够上传文件。其实curl远不止前面所说的那些功能,大家可以通过man curl阅读手册页获取更多的信息。类似的工具还有wget

curl命令使用了libcurl库来实现,libcurl库常用在C程序中用来处理HTTP请求,curlpp是libcurl的一个C++封装,这几个东西可以用在抓取网页、网络监控等方面的开发,而curl命令可以帮助来解决开发过程中遇到的问题。

常用参数

curl命令参数很多,这里只列出我曾经用过、特别是在shell脚本中用到过的那些。

-A:随意指定自己这次访问所宣称的自己的浏览器信息

-b/--cookie <name=string/file> cookie字符串或文件读取位置,使用option来把上次的cookie信息追加到http request里面去。

-c/--cookie-jar <file> 操作结束后把cookie写入到这个文件中

-C/--continue-at <offset>  断点续转

-d/--data <data>   HTTP POST方式传送数据

-D/--dump-header <file> 把header信息写入到该文件中

-F/--form <name=content> 模拟http表单提交数据

-v/--verbose 小写的v参数,用于打印更多信息,包括发送的请求信息,这在调试脚本是特别有用。

-m/--max-time <seconds> 指定处理的最大时长

-H/--header <header> 指定请求头参数

-s/--slient 减少输出的信息,比如进度

--connect-timeout <seconds> 指定尝试连接的最大时长

-x/--proxy <proxyhost[:port]> 指定代理服务器地址和端口,端口默认为1080

-T/--upload-file <file> 指定上传文件路径

-o/--output <file> 指定输出文件名称

--retry <num> 指定重试次数

-e/--referer <URL> 指定引用地址

-I/--head 仅返回头部信息,使用HEAD请求

-u/--user <user[:password]>设置服务器的用户和密码

-O:按照服务器上的文件名,自动存在本地

-r/--range <range>检索来自HTTP/1.1或FTP服务器字节范围

-T/--upload-file <file> 上传文件

使用示例

1,抓取页面内容到一个文件中

  [root@xi mytest]# curl -o home.html http://www.baidu.com   --将百度首页内容抓下到home.html中

     [root@xi mytest]#curl -o #2_#1.jpghttp://cgi2.tky.3web.ne.jp/~{A,B}/[001-201].JPG

           由于A/B下的文件名都是001,002...,201,下载下来的文件重名,这样,自定义出来下载下来的文件名,就变成了这样:原来: A/001.JPG —-> 下载后: 001-A.JPG 原来: B/001.JPG ---> 下载后: 001-B.JPG

2,用-O(大写的),后面的url要具体到某个文件,不然抓不下来。还可以用正则来抓取东西

  [root@xi mytest]# curl -O http://www.baidu.com/img/bdlogo.gif

         运行结果如下:

        % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                                                   Dload  Upload   Total   Spent    Left  Speed
       100  1575  100  1575    0     0  14940      0 --:--:-- --:--:-- --:--:-- 1538k

          会在当前执行目录中生成一张bdlogo.gif的图片。

  [root@xi mytest]# curl -O http://XXXXX/screen[1-10].JPG  --下载screen1.jpg~screen10.jpg

3,模拟表单信息,模拟登录,保存cookie信息

  [root@xi mytest]# curl -c ./cookie_c.txt -F log=aaaa -F pwd=******http://www.XXXX.com/wp-login.php

4,模拟表单信息,模拟登录,保存头信息

  [root@xi mytest]# curl -D ./cookie_D.txt -F log=aaaa -F pwd=******http://www.XXXX.com/wp-login.php

  -c(小写)产生的cookie和-D里面的cookie是不一样的。

5,使用cookie文件

  [root@xi mytest]# curl -b ./cookie_c.txt http://www.XXXX.com/wp-admin

6,断点续传,-C(大写)

  [root@xi mytest]# curl -C -O http://www.baidu.com/img/bdlogo.gif

7,传送数据,最好用登录页面测试,因为你传值过去后,curl回抓数据,你可以看到你传值有没有成功

  [root@xi mytest]# curl -d log=aaaa http://www.XXXX.com/wp-login.php

8,显示抓取错误,下面这个例子,很清楚的表明了。

  [root@xi mytest]# curl -fhttp://www.XXXX.com/asdf

  curl: (22) The requested URL returned error: 404

  [root@xi mytest]# curlhttp://www.XXXX.com/asdf

  <HTML><HEAD><TITLE>404,not found</TITLE>

9,伪造来源地址,有的网站会判断,请求来源地址,防止盗链。

  [root@xi mytest]# curl -ehttp://localhosthttp://www.XXXX.com/wp-login.php

10,当我们经常用curl去搞人家东西的时候,人家会把你的IP给屏蔽掉的,这个时候,我们可以用代理

  [root@xi mytest]# curl -x 24.10.28.84:32779 -o home.htmlhttp://www.XXXX.com

11,比较大的东西,我们可以分段下载

  [root@xi mytest]# curl -r 0-100 -o img.part1http://www.XXXX.com/wp-content/uploads/2010/09/compare_varnish.jpg

  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current

  Dload  Upload   Total   Spent    Left  Speed

  100   101  100   101    0     0    105      0 --:--:-- --:--:-- --:--:--     0

  [root@xi mytest]# curl -r 100-200 -o img.part2http://www.XXXX.com/wp-ontent/uploads/2010/09/compare_varnish.jpg

  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current

  Dload  Upload   Total   Spent    Left  Speed

  100   101  100   101    0     0     57      0  0:00:01  0:00:01 --:--:--     0

  [root@xi mytest]# curl -r 200- -o img.part3http://www.XXXX.com/wp-content/uploads/2010/09/compare_varnish.jpg

  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current

  Dload  Upload   Total   Spent    Left  Speed

  100  104k  100  104k    0     0  52793      0  0:00:02  0:00:02 --:--:-- 88961

  [root@xi mytest]# ls |grep part | xargs du -sh

  4.0K    one.part1

  112K    three.part3

  4.0K    two.part2

  用的时候,把他们cat一下就OK,cat img.part* >img.jpg

12,不会显示下载进度信息

  [root@xi mytest]# curl -s -o aaa.jpg http://www.baidu.com/img/bdlogo.gif

13,显示下载进度条

  [root@xi mytest]# curl  -0 http://www.baidu.com/img/bdlogo.gif     (以http1.0协议请求)

####################################################################### 100.0%

14,通过ftp下载文件

  [xifj@Xi ~]$ curl -u用户名:密码 -Ohttp://www.XXXX.com/demo/curtain/bbstudy_files/style.css

  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current

  Dload  Upload   Total   Spent    Left  Speed

  101  1934  101  1934    0     0   3184      0 --:--:-- --:--:-- --:--:--  7136

  [xifj@Xi ~]$ curl -u 用户名:密码 -O http://www.XXXX.com/demo/curtain/bbstudy_files/style.css

  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current

  Dload  Upload   Total   Spent    Left  Speed

  101  1934  101  1934    0     0   3184      0 --:--:-- --:--:-- --:--:--  7136

  或者用下面的方式

  [xifj@Xi ~]$ curl -O ftp://用户名:密码@ip:port/demo/curtain/bbstudy_files/style.css

  [xifj@Xi ~]$ curl -O ftp://用户名:密码@ip:port/demo/curtain/bbstudy_files/style.css

  15,通过ftp上传

  [xifj@Xi ~]$ curl -T test.sql ftp://用户名:密码@ip:port/demo/curtain/bbstudy_files/

  [xifj@Xi ~]$ curl -T test.sql ftp://用户名:密码@ip:port/demo/curtain/bbstudy_files/

15,模拟浏览器头

  [xifj@Xi ~]$ curl -A "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0)" -x 123.45.67.89:1080 -o page.html -D cookie0001.txthttp://www.www.baidu.com

16,PUT、GET、POST

比如 curl -T localfile http://cgi2.tky.3web.ne.jp/~zz/abc.cgi,这时候,使用的协议是HTTP的PUT method 
刚才说到PUT,自然想起来了其他几种methos--GET和POST。 
http提交一个表单,比较常用的是POST模式和GET模式 
GET模式什么option都不用,只需要把变量写在url里面就可以了
比如:
curl http://www.yahoo.com/login.cgi?user=nick&password=12345 
而POST模式的option则是 -d 
比如,curl -d "user=nick&password=12345" http://www.yahoo.com/login.cgi
就相当于向这个站点发出一次登陆申请~~~~~ 
到底该用GET模式还是POST模式,要看对面服务器的程序设定。 
一点需要注意的是,POST模式下的文件上的文件上传,比如
<form method="POST" enctype="multipar/form-data" action="http://cgi2.tky.3web.ne.jp/~zz/up_file.cgi">
<input type=file name=upload>
<input type=submit name=nick value="go">
</form>
这样一个HTTP表单,我们要用curl进行模拟,就该是这样的语法:
curl -F upload=@localfile -F nick=go http://cgi2.tky.3web.ne.jp/~zz/up_file.cgi 

1.访问http页面内容,输出到标准输出 curl http://www.neocanable.com 2.生成文件 curl -o index.html http://www.neocanable.com 以远程文件名保存 curl -O http://www.neocanable.com 参数-o为输出到某个文件,上面的命令等同于wget http://www.neocanable.com或者curl http://www.neocanable.com > index.html 3.添加proxy curl -x xxx.xxx.xxx.xxx http://www.neocanable.com 通过代理ip访问网页 4.添加浏览器信息 通常服务器的日志会记录客户端浏览器的信息 curl -A “浏览器信息” http://www.neocanable.com 5.批量下载文件 curl http://www.xxx.com/action/[1-100].html > /dev/null 这个最适合爬自己网站的缓存了 文件下载后重新命名和类正则使用,下载后的文件是demo1-001.html curl -o #1_#2 http://www.xxx.com/~{demo1,demo2}/[1-100].html 创建需要的目录 curl -o –create-dirs http://www.xxx.com/~{demo1,demo2}/[1-100].html 6.分块下载 curl -r 0-1024 http://www.xxx.com/aa.zip curl -r 1025- http://www.xxx.com/aa.zip 先下1M,然后再下剩下的 7.curl ftp 访问ftp地址 curl -u username:password ftp://www.xxx.com curl -u ftp://www.xxx.com 添加端口 curl -u username:password -P8899 ftp://www.xxx.com 上传文件到ftp curl -T /home/neo/demo.jpg -u username:password ftp://www.xxx.com 8.测试参数 测试站点相应时间 curl -o /dev/null -s -w %{time_connect}:%{time_starttransfer}:%{time_total} www.google.com 查看http_code curl -o /dev/null -s -w %{http_code} http://www.neocanable.com 网页或文件大小 curl -o /dev/null -s -w %{size_header} http://www.neocanable.com http_code:http返回类似404,200,500等 time_total:总相应时间 time_namelookup:域名解析时间 time_connect:连接到目标地址耗费的时间 time_pretransfer:从执行到开始传输文件的时间间隔 time_starttransfer:从执行到开始传输文件的时间间隔 size_download:下载网页或文件大小 size_upload:上传文件大小 size_header:响应头 size_request:发送请求参数大小 speed_download:传输速度 speed_upload:平均上传速度 content_type:下载文件类型. (Added in 7.9.5) 9.postget请求 get请求 curl “param1=name&params2=pass” http://www.xxx.com post请求 curl -d “param1=name&params2=pass” http://www.xxx.com 10.响应超时 curl -m 40 http://www.xxx.com curl –timeout 40 http://www.xxx.com 11.破解网站的防盗链 curl -e “http://www.a.net” http://www.b.net/acion 12.网站头部信息 curl -I http://www.neocanable.com 13.更总url跳转 curl -L http://url.cn/2yQFfd 14.正确的给url编码 curldata-urlencode http://www.xxx.com/action?name=张三&sex=男 15.限制url的传输速度 curl –limit-rate http://www.xxx.com/action 16.限制下载文件大小 curl –max-filesize 1024 http://www.xxx.com/action 超过1M将不执行操作,并且返回出错 17.curl错误代码 1:未支持的协议。此版cURL不支持这一协议。 2:初始化失败。 3:URL格式错误。语法不正确。 5:无法解析代理。无法解析给定代理主机。 6:无法解析主机。无法解析给定的远程主机。 7:无法连接到主机。 8:FTP非正常的服务器应答。cURL无法解析服务器发送的数据。 9:FTP访问被拒绝。服务器拒绝登入或无法获取您想要的特定资源或目录。最有可能的是您试图进入一个在此服务器上不存在的目录。 11:FTP 非正常的PASS回复。cURL无法解析发送到PASS请求的应答。 13:FTP 非正常的的PASV应答,cURL无法解析发送到PASV请求的应答。 14:FTP非正常的227格式。cURL无法解析服务器发送的227行。 15:FTP无法连接到主机。无法解析在227行中获取的主机IP。 17:FTP无法设定为二进制传输。无法改变传输方式到二进制。 18:部分文件。只有部分文件被传输。 19:FTP不能下载/访问给定的文件, RETR (或类似)命令失败。 21:FTP quote错误。quote命令从服务器返回错误。 22:HTTP 找不到网页。找不到所请求的URL或返回另一个HTTP 400或以上错误。此返回代码只出现在使用了-f/–fail选项以后。 23:写入错误。cURL无法向本地文件系统或类似目的写入数据。 25:FTP 无法STOR文件。服务器拒绝了用于FTP上传的STOR操作。 26:读错误。各类读取问题。 27:内存不足。内存分配请求失败。 28:操作超时。到达指定的超时期限条件。 30:FTP PORT失败。PORT命令失败。并非所有的FTP服务器支持PORT命令,请尝试使用被动(PASV)传输代替! 31:FTP无法使用REST命令。REST命令失败。此命令用来恢复的FTP传输。 33:HTTP range错误。range “命令”不起作用。 34:HTTP POST错误。内部POST请求产生错误。 35:SSL连接错误。SSL握手失败。 36:FTP 续传损坏。不能继续早些时候被中止的下载。 37:文件无法读取。无法打开文件。权限问题? 38:LDAP 无法绑定。LDAP绑定(bind)操作失败。 39:LDAP 搜索失败。 41:功能无法找到。无法找到必要的LDAP功能。 42:由回调终止。应用程序告知cURL终止运作。 43:内部错误。由一个不正确参数调用了功能。 45:接口错误。指定的外发接口无法使用。 47:过多的重定向。cURL达到了跟随重定向设定的最大限额跟 48:指定了未知TELNET选项。 49:不合式的telnet选项。 51:peer的SSL证书或SSH的MD5指纹没有确定。 52:服务器无任何应答,该情况在此处被认为是一个错误。 53:找不到SSL加密引擎。 54:无法将SSL加密引擎设置为默认。 55:发送网络数据失败。 56:在接收网络数据时失败。 58:本地证书有问题。 59:无法使用指定的SSL密码。 60:peer证书无法被已知的CA证书验证。 61:无法辨识的传输编码。 62:无效的LDAP URL。 63:超过最大文件尺寸。 64:要求的FTP的SSL水平失败。 65:发送此数据需要的回卷(rewind)失败。 66:初始化SSL引擎失败。 67:用户名、密码或类似的信息未被接受,cURL登录失败。 68:在TFTP服务器上找不到文件。 69:TFTP服务器权限有问题。 70:TFTP服务器磁盘空间不足。 71:非法的TFTP操作。 72:未知TFTP传输编号(ID)。 73:文件已存在(TFTP) 。 74:无此用户(TFTP) 。 75:字符转换失败。 76:需要字符转换功能。 77:读SSL证书出现问题(路径?访问权限? ) 。 78:URL中引用的资源不存在。 79:SSH会话期间发生一个未知错误。 80:未能关闭SSL连接。 82:无法加载CRL文件,丢失或格式不正确(在7.19.0版中增加 ) 。 83:签发检查失败(在7.19.0版中增加 ) 。
我有一个ai助手主程序,现在我需要制作ui部分,我有一个api工具 # api_server.py import threading import time from flask import Flask, request, jsonify from werkzeug.serving import make_server from flask_cors import CORS # pip install flask-cors # 假设这些函数来自你的主程序模块 # from Progress.app import get_ai_assistant, get_task_executor, get_tts_engine, get_voice_recognizer # --- 全局状态共享 --- current_status = { "is_listening": False, "is_tts_playing": False, "current_timeout": 8.0, "last_command_result": None, "timestamp": int(time.time()) } # --- 模拟服务实例(实际项目中应替换为真实对象)--- class MockAssistant: def process_voice_command(self, text): return {"action": "mock_action", "target": text} class MockExecutor: def execute_task_plan(self, plan): return {"success": True, "message": f"已执行 {plan['target']}", "data": {}} class MockTTS: def speak(self, text, async_mode=False): print(f"[TTS] 正在播报: {text}") if not async_mode: time.sleep(1) # 模拟播放延迟 else: def _async_play(): time.sleep(1) threading.Thread(target=_async_play, daemon=True).start() class MockRecognizer: def start_listening(self): global current_status current_status["is_listening"] = True current_status["timestamp"] = int(time.time()) print("🎙️ 开始监听用户语音...") def stop_listening(self): global current_status current_status["is_listening"] = False current_status["timestamp"] = int(time.time()) # 实例化模拟组件(上线时替换为真实导入) assistant = MockAssistant() executor = MockExecutor() tts_engine = MockTTS() recognizer = MockRecognizer() class APIServer: def __init__(self): self.app = Flask(__name__) CORS(self.app) # ✅ 启用跨域支持 self.server = None self.thread = None self.running = False self._add_routes() def _update_status(self, **kwargs): """更新全局状态""" current_status.update(kwargs) current_status["timestamp"] = int(time.time()) def _add_routes(self): """注册所有 API 路由""" # 1. GET /api/health - 健康检查 @self.app.route('/api/health', methods=['GET']) def health(): return jsonify({ "status": "ok", "service": "ai_assistant", "timestamp": int(time.time()) }) # 2. GET /api/status - 查询当前状态 @self.app.route('/api/status', methods=['GET']) def status(): return jsonify({**current_status}) # 3. POST /api/start - 启动助手 @self.app.route('/api/start', methods=['POST']) def start(): # 可以触发一些初始化逻辑 self._update_status(is_listening=True) return jsonify({ "status": "running", "message": "AI 助手已就绪", "features": ["voice", "tts", "file", "app_control"], "timestamp": int(time.time()) }) # 4. POST /api/command - 执行用户指令 @self.app.route('/api/command', methods=['POST']) def handle_command(): try: data = request.get_json() if not data or 'text' not in data: return jsonify({ "success": False, "response_to_user": "未收到有效指令" }), 400 text = data['text'] context = data.get('context', {}) options = data.get('options', {}) should_speak = options.get('should_speak', True) return_plan = options.get('return_plan', False) print(f"📩 收到命令: '{text}' | 上下文: {context}") # 👇 调用 AI 助手核心逻辑(请替换为你的真实模块) # from Progress.app import get_ai_assistant, get_task_executor, get_tts_engine # assistant = get_ai_assistant() # executor = get_task_executor() # tts = get_tts_engine() decision = assistant.process_voice_command(text) result = executor.execute_task_plan(decision) ai_reply = result["message"] if not result["success"] and not ai_reply.startswith("抱歉"): ai_reply = f"抱歉,{ai_reply}" # 更新状态:正在处理 self._update_status( is_processing=True, last_command_result={"success": result["success"], "message": ai_reply, "operation": decision.get("action")} ) # 异步播报 if should_speak: self._update_status(is_tts_playing=True) tts_engine.speak(ai_reply, async_mode=True) # 模拟 TTS 结束后恢复状态 def _finish_tts(): time.sleep(1) self._update_status(is_tts_playing=False) threading.Thread(target=_finish_tts, daemon=True).start() # 构造响应 response_data = { "success": result["success"], "response_to_user": ai_reply, "operation": decision.get("action"), "details": result, "should_speak": should_speak, "timestamp": int(time.time()), } if return_plan: response_data["plan"] = decision self._update_status(is_processing=False) return jsonify(response_data), 200 except Exception as e: print(f"❌ 处理命令失败: {e}") return jsonify({ "success": False, "error": str(e), "timestamp": int(time.time()) }), 500 # 5. POST /api/tts/speak - 主动播报语音 @self.app.route('/api/tts/speak', methods=['POST']) def speak(): try: data = request.get_json() if not data or 'text' not in data: return jsonify({"error": "Missing 'text'"}), 400 text = data['text'] print(f"[TTS] 请求播报: {text}") self._update_status(is_tts_playing=True) tts_engine.speak(text, async_mode=True) def _finish(): time.sleep(1) self._update_status(is_tts_playing=False) threading.Thread(target=_finish, daemon=True).start() return jsonify({ "status": "speaking", "text": text, "timestamp": int(time.time()) }) except Exception as e: return jsonify({"error": str(e)}), 500 # 6. POST /api/wakeup - 远程唤醒 @self.app.route('/api/wakeup', methods=['POST']) def wakeup(): try: data = request.get_json() or {} device = data.get("device", "unknown") location = data.get("location", "unknown") print(f"🔔 远程唤醒信号来自 {device} @ {location}") # 播放提示音(可通过 pygame 或 winsound 实现) print("💡 滴—— 唤醒成功!") # 设置为倾听模式 recognizer.start_listening() self._update_status(is_listening=True) return jsonify({ "status": "ready", "message": "已进入倾听模式", "timestamp": int(time.time()) }) except Exception as e: return jsonify({"error": str(e)}), 500 def start(self, host="127.0.0.1", port=5000, debug=False): """启动 API 服务(非阻塞)""" if self.running: print("⚠️ API 服务器已在运行") return try: self.server = make_server(host, port, self.app) self.running = True def run_flask(): print(f"🌐 AI 助手 API 已启动 → http://{host}:{port}/api") self.server.serve_forever() self.thread = threading.Thread(target=run_flask, daemon=True) self.thread.start() except Exception as e: print(f"❌ 启动 API 服务失败: {e}") raise def stop(self): """关闭 API 服务""" if not self.running: return print("🛑 正在关闭 API 服务...") try: self.server.shutdown() except: pass finally: self.running = False if self.thread: self.thread.join(timeout=3) if self.thread.is_alive(): print("⚠️ API 服务线程未能及时退出") print("✅ API 服务已关闭") # ----------------------------- # 使用示例 # ----------------------------- if __name__ == '__main__': api = APIServer() api.start() try: while True: time.sleep(1) except KeyboardInterrupt: api.stop() 和一个api文档 # 🌐 AI 助手 API 接口文档 服务地址: http://127.0.0.1:5000/api 协议: HTTP/HTTPS 编码: UTF-8 内容类型: application/json 跨域支持: ✅ 已启用 CORS # 🔑 认证方式(可选)暂时不加密钥 目前为本地服务,默认信任内网调用。 如需安全增强,请添加: http X-API-Key: your-secret-token 或使用 HTTPS + IP 白名单。 # 📚 接口列表 路径 方法 功能 /health GET 健康检查 /status GET 获取当前运行状态 /start POST 启动或唤醒助手 /command POST 发送自然语言指令 /tts/speak POST 主动播放语音 /wakeup POST 远程唤醒信号 1. GET /api/health - 健康检查 💡 描述 检查后端服务是否正常运行。 请求示例 http GET /api/health 成功响应 { "status": "ok", "service": "ai_assistant", "timestamp": 1719876543 } 字段 类型 说明 status string 固定为 "ok" 表示存活 service string 服务名称 timestamp number 当前时间戳 ✅ 用途:Flutter App 启动时探测服务是否存在。 2. GET /api/status - 查询当前状态 💡 描述 获取语音识别器和 TTS 的实时状态。 请求示例 http GET /api/status 成功响应 { "is_listening": true, "is_tts_playing": false, "current_timeout": 8, "last_command_result": { "success": true, "message": "正在播放音乐", "operation": "play_music" }, "timestamp": 1719876543 } 字段 类型 说明 is_listening boolean 是否正在监听麦克风 is_tts_playing boolean 是否正在播报语音 current_timeout float 下一次监听超时时间(秒) last_command_result object 上一条命令执行结果 timestamp number 时间戳 📌 用途: 控制前端 UI 显示“AI 正在说话” 防止冲突收音 3. POST /api/start - 启动助手 💡 描述 用于前端点击“启动”按钮时触发。如果服务已运行则返回状态。 ⚠️ 注意:不能真正“启动一个进程”,但可以确认服务就绪。 请求示例 http POST /api/start Content-Type: application/json json {} 成功响应 { "status": "running", "message": "AI 助手已就绪", "features": ["voice", "tts", "file", "app_control"], "timestamp": 1719876543 } 字段 类型 说明 status string "running" message string 提示信息 features array 支持的功能列表 timestamp number 当前时间戳 4. POST /api/command - 执行用户指令(核心接口) 💡 描述 发送一条自然语言命令,走完整 AI 决策 → 执行 → 播报流程。 请求参数 { "text": "打开记事本", "context": { "user_id": "U123", "device": "phone", "location": "bedroom" }, "options": { "should_speak": true, "return_plan": false } } 参数 类型 必填 说明 text string ✅ 用户输入的自然语言文本 context object ❌ 上下文信息(可用于日志追踪) options.should_speak boolean ❌ 是否让 TTS 播报结果(默认 true) options.return_plan boolean ❌ 是否返回详细的执行计划(调试用) 成功响应 { "success": true, "response_to_user": "已为您打开记事本", "operation": "open_app", "details": { "app_name": "notepad" }, "should_speak": true, "plan": { ... }, // 仅当 return_plan=true 时存在 "timestamp": 1719876543 } 字段 类型 说明 success boolean 执行是否成功 response_to_user string 要对用户说的话 operation string 主要操作类型(如 open_app) details object 操作详情 should_speak boolean 是否应播报语音 plan object 完整任务计划(仅当开启时返回) timestamp number 时间戳 错误响应(400 Bad Request) { "success": false, "response_to_user": "未收到有效指令" } 5. POST /api/tts/speak - 主动播报语音 💡 描述 不经过 AI 决策,直接让助手说出一句话。 请求示例 http POST /api/tts/speak Content-Type: application/json json { "text": "您的会议将在五分钟后开始" } 成功响应 { "status": "speaking", "text": "您的会议将在五分钟后开始", "timestamp": 1719876543 } 字段 类型 说明 status string "speaking" text string 正在播报的内容 timestamp number 时间戳 📌 用途:通知、提醒、异常报警等场景。 6. POST /api/wakeup - 远程唤醒 💡 描述 当手机检测到“小智小智”唤醒词后,发送此请求通知电脑准备接收指令。 请求示例 http POST /api/wakeup Content-Type: application/json 设备位置 { "device": "phone", "location": "living_room" } 成功响应 { "status": "ready", "message": "已进入倾听模式", "timestamp": 1719876543 } 后端行为 设置 recognizer.is_listening = True 可播放提示音“滴”一声表示唤醒成功 📌 注意:建议配合 /api/command 使用,唤醒后再发命令。 🧪 测试建议(使用 curl) # 检查健康 curl http://127.0.0.1:5000/api/health # 唤醒 curl -X POST http://127.0.0.1:5000/api/wakeup # 发送命令 curl -X POST http://127.0.0.1:5000/api/command \ -H "Content-Type: application/json" \ -d '{"text": "打开浏览器"}' # 播报语音 curl -X POST http://127.0.0.1:5000/api/tts/speak \ -H "Content-Type: application/json" \ -d '{"text": "你好,我是你的语音助手"}' ✅ 最佳实践建议 项目 建议 🔐 安全性 生产环境加 Token 或 HTTPS 📦 打包部署 使用 PyInstaller 打包成 .exe 并设置开机自启 🔄 状态同步 前端轮询 /api/status 判断是否可交互 📈 日志记录 记录所有 /api/command 调用用于调试 🧭 唤醒策略 手机端做离线唤醒词检测,再发 /api/wakeup 帮我选择GUI框架,并给我所有代码
最新发布
10-28
你已经有一个功能完整的 **AI 助手后端服务(Flask API)**,现在需要为它构建一个前端 GUI 界面。你的目标是: - 与 `http://127.0.0.1:5000/api` 通信 - 实现状态监控、语音唤醒、发送指令、TTS 播报等功能 - 运行在本地桌面环境(Windows/macOS/Linux--- ## ✅ 推荐的 GUI 框架:**Electron + React/Vue(TypeScript)** ### 🎯 为什么选择 Electron? | 优势 | 说明 | |------|------| | 跨平台 | 支持 Windows / macOS / Linux | | 易于调用 API | 前端 JS 天然支持 HTTP 请求 | | 可访问麦克风 | 使用 Web Audio API 或 `navigator.mediaDevices.getUserMedia()` | | UI 美观现代 | 可使用 TailwindCSS / Material UI 快速开发 | | 社区强大 | 大量组件和插件可用 | | 与 Flask 后端完美配合 | 前后端分离架构清晰 | > ⚠️ 注意:Electron 打包体积较大,但适合桌面 AI 助手这类“富媒体交互”应用。 --- 如果你更偏好 **纯 Python 技术栈**,也可以考虑: - `PyQt5/6` + `QWebEngineView`(嵌入网页) - `Tkinter`(太简陋,不推荐) - `Flet`(由 Flutter 团队开发,运行在 WebView 上) 但综合来看,**Electron 是最佳选择**。 --- ## 🧱 架构概览 ``` +------------------+ HTTP +--------------------+ | Electron App | <--------------> | Flask API (Python) | | (Frontend + UI) | http://localhost:5000 | (Voice, TTS, Logic) | +------------------+ +--------------------+ ``` --- # 💻 完整代码实现:基于 Electron + React + TypeScript 的 AI 助手 GUI 我们将使用: - **Vite + React + TypeScript** 作为前端框架 - **Electron** 包装成桌面应用 - **Axios** 发送请求到 Flask API - **SpeechRecognition API** 实现语音输入(浏览器原生) --- ## 📁 项目结构 ```bash ai-assistant-ui/ ├── public/ │ └── icon.png ├── src/ │ ├── App.tsx │ ├── main.tsx │ ├── components/ │ │ ├── StatusPanel.tsx │ │ ├── CommandInput.tsx │ │ ├── TTSSpeaker.tsx │ │ └── VoiceRecorder.tsx │ └── utils/api.ts ├── vite.config.ts ├── electron-main.js ├── package.json └── tsconfig.json ``` --- ## 1. 初始化项目 ```bash npm create vite@latest ai-assistant-ui --template react-ts cd ai-assistant-ui npm install electron electron-is-dev concurrently wait-on --save-dev npm install axios @mui/material @emotion/react @emotion/styled ``` --- ## 2. `package.json` ```json { "name": "ai-assistant-ui", "version": "1.0.0", "main": "electron-main.js", "scripts": { "dev": "concurrently \"vite\" \"wait-on http://localhost:3000 && electron electron-main.js\"", "build": "tsc && vite build", "preview": "vite preview" }, "dependencies": { "axios": "^1.6.0", "@mui/material": "^5.14.0", "@emotion/react": "^11.11.0", "@emotion/styled": "^11.11.0" }, "devDependencies": { "vite": "^5.0.0", "typescript": "^5.0.0", "electron": "^28.0.0", "electron-is-dev": "^2.0.0", "concurrently": "^8.0.0", "wait-on": "^7.0.0" } } ``` --- ## 3. `vite.config.ts` ```ts import { defineConfig } from 'vite'; import react from '@vitejs/plugin-react'; export default defineConfig({ plugins: [react()], server: { port: 3000, strictPort: true, host: 'localhost' }, build: { outDir: 'dist' } }); ``` --- ## 4. `tsconfig.json` ```json { "compilerOptions": { "target": "ES2020", "lib": ["DOM", "DOM.Iterable", "ES6"], "module": "ESNext", "esModuleInterop": true, "jsx": "react-jsx", "strict": true, "skipLibCheck": true, "outDir": "dist" }, "include": ["src"] } ``` --- ## 5. `src/utils/api.ts` ```ts import axios from 'axios'; const API_BASE = 'http://127.0.0.1:5000/api'; export const apiClient = axios.create({ baseURL: API_BASE, timeout: 10000, }); // Health Check export const checkHealth = () => apiClient.get('/health'); // Get current status export const getStatus = () => apiClient.get('/status'); // Start assistant export const startAssistant = () => apiClient.post('/start'); // Send command export const sendCommand = (text: string, options = {}) => apiClient.post('/command', { text, context: { device: 'desktop' }, options: { should_speak: true, return_plan: false, ...options } }); // Speak directly export const speakText = (text: string) => apiClient.post('/tts/speak', { text }); // Wake up export const wakeup = () => apiClient.post('/wakeup', { device: 'desktop', location: 'local' }); ``` --- ## 6. `src/components/StatusPanel.tsx` ```tsx import { useEffect, useState } from 'react'; import { Box, Typography, Paper, CircularProgress } from '@mui/material'; import { getStatus } from '../utils/api'; interface Status { is_listening: boolean; is_tts_playing: boolean; last_command_result: any; timestamp: number; } export default function StatusPanel() { const [status, setStatus] = useState<Status | null>(null); const [loading, setLoading] = useState(true); const fetchStatus = async () => { try { const res = await getStatus(); setStatus(res.data); } catch (err) { setStatus(null); } finally { setLoading(false); } }; useEffect(() => { fetchStatus(); const interval = setInterval(fetchStatus, 2000); // 每2秒刷新一次 return () => clearInterval(interval); }, []); if (loading) return <CircularProgress size={20} />; return ( <Paper sx={{ p: 2, mb: 2 }}> <Typography variant="h6">运行状态</Typography> <Box mt={1}> <Typography>🎙️ 监听: <strong>{status?.is_listening ? '是' : '否'}</strong></Typography> <Typography>📢 播报: <strong>{status?.is_tts_playing ? '是' : '否'}</strong></Typography> {status?.last_command_result && ( <Typography variant="body2" color="text.secondary"> 最后结果: {status.last_command_result.message} </Typography> )} </Box> </Paper> ); } ``` --- ## 7. `src/components/VoiceRecorder.tsx` ```tsx import { useState, useRef } from 'react'; import { Button, IconButton } from '@mui/material'; import { Mic, Stop } from '@mui/icons-material'; import { wakeup, sendCommand } from '../utils/api'; declare global { interface Window { SpeechRecognition: any; webkitSpeechRecognition: any; } } const SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition; export default function VoiceRecorder() { const [isListening, setIsListening] = useState(false); const recognitionRef = useRef<any>(null); const startListening = () => { if (!SpeechRecognition) { alert("当前浏览器不支持语音识别"); return; } // 先远程唤醒 wakeup().catch(console.error); const recognition = new SpeechRecognition(); recognition.lang = 'zh-CN'; recognition.interimResults = false; recognition.maxAlternatives = 1; recognition.onresult = (event: any) => { const transcript = event.results[0][0].transcript; console.log('🗣️ 识别结果:', transcript); sendCommand(transcript).catch(console.error); }; recognition.onerror = (event: any) => { console.error('🎤 语音识别错误:', event.error); setIsListening(false); }; recognition.onend = () => { setIsListening(false); }; recognition.start(); recognitionRef.current = recognition; setIsListening(true); }; const stopListening = () => { if (recognitionRef.current) { recognitionRef.current.stop(); } setIsListening(false); }; return ( <div> <Button variant="contained" color="primary" startIcon={isListening ? <Stop /> : <Mic />} onClick={isListening ? stopListening : startListening} > {isListening ? '停止录音' : '开始录音'} </Button> </div> ); } ``` --- ## 8. `src/components/CommandInput.tsx` ```tsx import { useState } from 'react'; import { TextField, Button, Box } from '@mui/material'; import { sendCommand } from '../utils/api'; export default function CommandInput() { const [input, setInput] = useState(''); const [response, setResponse] = useState(''); const handleSubmit = async () => { if (!input.trim()) return; try { const res = await sendCommand(input); setResponse(res.data.response_to_user); } catch (err: any) { setResponse(`出错: ${err.message}`); } }; return ( <Box my={2}> <TextField label="输入指令" fullWidth value={input} onChange={(e) => setInput(e.target.value)} onKeyPress={(e) => e.key === 'Enter' && handleSubmit()} /> <Box mt={1}> <Button variant="contained" color="primary" onClick={handleSubmit}> 发送 </Button> </Box> {response && ( <Box mt={2} p={2} bgcolor="#f0f0f0" borderRadius={4}> <strong>AI 回复:</strong> {response} </Box> )} </Box> ); } ``` --- ## 9. `src/components/TTSSpeaker.tsx` ```tsx import { useState } from 'react'; import { TextField, Button, Box } from '@mui/material'; import { speakText } from '../utils/api'; export default function TTSSpeaker() { const [text, setText] = useState(''); const handleSpeak = async () => { if (!text.trim()) return; await speakText(text); setText(''); }; return ( <Box my={2}> <TextField label="播报内容" fullWidth value={text} onChange={(e) => setText(e.target.value)} placeholder="比如:今天的天气很好" /> <Box mt={1}> <Button variant="outlined" color="secondary" onClick={handleSpeak}> 播报语音 </Button> </Box> </Box> ); } ``` --- ## 10. `src/App.tsx` ```tsx import { Container, Typography, Box } from '@mui/material'; import StatusPanel from './components/StatusPanel'; import VoiceRecorder from './components/VoiceRecorder'; import CommandInput from './components/CommandInput'; import TTSSpeaker from './components/TTSSpeaker'; export default function App() { return ( <Container maxWidth="sm" sx={{ mt: 4 }}> <Typography variant="h4" component="h1" gutterBottom> 🤖 AI 助手控制台 </Typography> <StatusPanel /> <Box my={2}> <Typography variant="h6">🎙️ 语音控制</Typography> <VoiceRecorder /> </Box> <Box my={2}> <Typography variant="h6">💬 手动输入</Typography> <CommandInput /> </Box> <Box my={2}> <Typography variant="h6">📢 主动播报</Typography> <TTSSpeaker /> </Box> </Container> ); } ``` --- ## 11. `src/main.tsx` ```tsx import React from 'react'; import ReactDOM from 'react-dom/client'; import App from './App'; ReactDOM.createRoot(document.getElementById('root')!).render( <React.StrictMode> <App /> </React.StrictMode> ); ``` --- ## 12. `electron-main.js` ```js const { app, BrowserWindow } = require('electron'); const isDev = require('electron-is-dev'); const path = require('path'); function createWindow() { const win = new BrowserWindow({ width: 800, height: 600, webPreferences: { nodeIntegration: false, }, icon: path.join(__dirname, 'public/icon.png') }); const url = isDev ? 'http://localhost:3000' : `file://${path.join(__dirname, 'dist/index.html')}`; win.loadURL(url); if (isDev) { win.webContents.openDevTools(); } } app.whenReady().then(() => { createWindow(); app.on('activate', () => { if (BrowserWindow.getAllWindows().length === 0) createWindow(); }); }); app.on('window-all-closed', () => { if (process.platform !== 'darwin') app.quit(); }); ``` --- ## 13. 启动方式 确保 Flask 后端已运行: ```bash python api_server.py ``` 然后启动 Electron 前端: ```bash npm run dev ``` --- ## ✅ 功能演示 | 功能 | 是否支持 | |------|----------| | 启动 Flask 并轮询状态 | ✅ | | 显示监听/TTS 状态 | ✅ | | 点击按钮录音并转文本 | ✅ | | 发送命令给 AI 并获取回复 | ✅ | | 主动让 AI 说话(TTS) | ✅ | | 自动播放 AI 回复 | ✅(通过 `/api/command` 中 `should_speak` 控制) | --- ## 📦 打包发布(可选) 安装打包工具: ```bash npm install --save-dev @electron/build-tools ``` 或使用 `electron-builder`: ```bash npm install electron-builder --save-dev ``` 配置 `"build"` 字段并运行: ```json "build": { "productName": "AI Assistant", "win": { "target": "nsis" }, "mac": { "target": "dmg" }, "linux": { "target": "AppImage" } } ``` ```bash npx electron-builder --dir # 开发测试 npx electron-builder # 打包正式版 ``` --- ## 🔐 安全建议(上线时) - 添加 `X-API-Key` 到每个请求- 使用 HTTPS(可通过 Nginx 反向代理) - 设置 IP 白名单(如仅允许 `127.0.0.1`) --- ## ✅ 总结 你现在拥有了一个完整的 **Electron + React + TypeScript** 编写的 AI 助手 GUI,具备以下能力: - 实时状态监控 - 语音识别 + 文本输入双模式 - 与 Flask API 完美对接 - 跨平台桌面部署 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值