├── .gitignore
├── image.png
├── ocr.png
├── LICENSE
├── README.md
└── worker.js
/.gitignore:
--------------------------------------------------------------------------------
1 | .idea/
2 |
--------------------------------------------------------------------------------
/image.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Cunninger/ocr-based-qwen/HEAD/image.png
--------------------------------------------------------------------------------
/ocr.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Cunninger/ocr-based-qwen/HEAD/ocr.png
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | 重要声明
2 | 项目性质:本项目是对 QwenLM 的逆向工程实现,仅供学习和研究使用。
3 | 免责声明:任何商业用途或滥用行为均与作者无关。
4 | 法律合规:使用者需遵守相关法律法规和平台的使用条款。
5 |
6 | 开源协议 (LICENSE)
7 | 本项目采用 MIT 许可证,明确限制仅用于学习和研究目的。以下是 LICENSE 文件内容的补充说明:
8 |
9 | MIT License
10 | Copyright (c) 2025 [cunninger]
11 | Permission is hereby granted, free of charge, to any person obtaining a copy
12 | of this software and associated documentation files (the "Software"), to deal
13 | in the Software without restriction, including without limitation the rights
14 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
15 | copies of the Software, and to permit persons to whom the Software is
16 | furnished to do so, subject to the following conditions:
17 |
18 | 1. The Software is provided for educational and research purposes only.
19 | Commercial use is strictly prohibited.
20 |
21 | 2. The above copyright notice and this permission notice shall be included in all
22 | copies or substantial portions of the Software.
23 |
24 | 3. If you modify or distribute the Software, you must include a reference to the original project's open-source address: https://github.com/Cunninger/ocr-based-qwen.
25 |
26 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
27 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
28 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
29 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
30 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
31 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
32 | SOFTWARE.
33 |
34 | 补充说明
35 | 二次开发声明:如果您对项目进行二次开发或分发,必须声明本项目的开源地址:https://github.com/Cunninger/ocr-based-qwen。
36 | 请确保在使用或修改本项目时遵守上述条款和条件。
37 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | # QwenLM OCR
6 |
7 |
8 | 本项目基于 [QwenLM](https://chat.qwenlm.ai/) 。通过调用 QwenLM 的 ”API“,你可以从图片中提取文字内容,并且该项目支持一键部署到 **Cloudflare Workers** (CF) 上。
9 |
10 | ## 项目展示
11 | 
12 |
13 |
14 | ## 测试cookie
15 | - 如果出现**处理失败: 文件上传失败的错误**,说明测试Cookie 上传文件过多, 尝试获取自己账号的Cookie 使用
16 | ```
17 | {
18 | "code": "RateLimited",
19 | "detail": "Reached file upload limited: too many files uploaded in (86400.0) seconds."
20 | }
21 | ```
22 |
23 | 
24 | - cookie1:
25 | ```txt
26 | _gcl_au=1.1.1128300923.1746849193; _bl_uid=m1mL2atCh8Cot5wsRoIFbqdhm7tg; xlly_s=1; cna=qrulIGTI1xgCAbZmOSlWpOi2; cnaui=53e94788-c03d-4682-993a-a4d3c4e2d649; aui=53e94788-c03d-4682-993a-a4d3c4e2d649; acw_tc=0a03e55a17471050174934469e5698d16b1ed504f9e9c68f410d550889e99b; x-ap=ap-southeast-1; sca=54887197; atpsida=3cac89c5d9360e40b62eb508_1747105020_1; ssxmod_itna=eqIxgDnQKYqDqOD4kxjxYKD5i=m6l40QDXDUqAjdGgDYq7=GF7DChzw1CDBKeZxv=t7iiHP4gRxGXxkKxiNDAxq0iDC8eQeOCmL+tGqFjmhtqqRZayb5zHF2Ghm85mnrZpswVMZYKwDCPDExGkeBvhwDiiax0rD0eDPxDYDGbaD7PDoxDrOIYDjYoICSEmQz4DKx0kDY5Dw1+mDYPDWxDFb+DktYEwbDDCDivfV83DixiaaSDDBg0u4pxmtDi3bUOGjz+LxT0IhooD9E4Ds6xgl/kgvMiAkS17Wvewff3DvxDk2IgGUtTpwfTo+8ZxxohxY7eChxO7ek7orYqoD4aYhoA4ti517qoldaGQQDwolQChmw073QpCxxY+zyTsb+s/ke=eKn2tBjtwGYheYaYqpxoMA5VYYqr5OBQkDtaW5Q7q/2x4D; ssxmod_itna2=eqIxgDnQKYqDqOD4kxjxYKD5i=m6l40QDXDUqAjdGgDYq7=GF7DChzw1CDBKeZxv=t7iiHP4gKxDfrtK2nguI=5mwvNG4oIhfnAQPD; SERVERID=8d1f4d10c35f5d9c380a0c17580aeca9|1747105025|1747105017; token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjUzZTk0Nzg4LWMwM2QtNDY4Mi05OTNhLWE0ZDNjNGUyZDY0OSIsImV4cCI6MTc0OTY5NzAyNX0.MZ-Nmyq08Oy0vDR_PdWDpvq8bAfuA371KPA2fez8Qz0; isg=BNnZ9u4y1WqSC4nCppf6kGHW6MWzZs0YnUIs8PuOVYB_AvmUQ7bd6EcUBMZ0jmVQ; tfstk=gySjtnM1FjcXkNttCE2ydgAaio-_L8rehA9OKOnqBnKv1A1NpFpq0-b1P15XM16gnddO61ONuzrFntxMXWP_YkWc0MUDzitt_TKDQGHyzvEFntDRrOQ1nkk6PJ-6XCC9kU3JpQpxM1LvwUdkIdhvBme7FQvM6Kn9XULJ3pJ9XhCOe89MwKK9k1B-pmGeCRRfh-x3JMyTHBXvNcnO2lYXOt3ZXcIWhE6dHQ6PUg9XlBL9I3NdD_WO0iYuOXKGUw11W14Ern6OknpFnoiWvtQPfLSaQVOAig6R2UMEYh_AGNLhPk0HkFOfyiLSBcBkfaLd5sNjoIQVNevJyAZHU6RRniQ7IbBATItveUrLdTLOziYh07nXvwX20NCg84xfBZK54MmeOK3-5YTnfLOUF8giSdRn6tlqKjZvkLvXV8wSG3LvELOUF8giSEpkU4w7FjtR.
27 | ```
28 | - cookie2:
29 | ```txt
30 | cna=jVVkIH8WwiECAWrgu3Rh49BZ; _bl_uid=69m7O83qin4t1v50n9b33n9oge32; cnaui=1df8319d-47e8-4859-b77f-810584faec0f; aui=1df8319d-47e8-4859-b77f-810584faec0f; _gcl_au=1.1.505594525.1742563213.867342246.1745928027.1745928034; acw_tc=0a03e55f17471053364778166e659f6b4859f09675a78f141a3e9cc6228e4a; x-ap=ap-southeast-1; sca=73b49ddd; xlly_s=1; ssxmod_itna=GqGx9DyDuiiQYY5i7=YAKG7qDOG2ikDnQDBP01DphxQyK08D6DYqGdcFkmNhY=eF34oqrYqSnxGXKFYxiNDAxq0iDC+ejrenpBHw3354EhteeiAl0g5Ir825b40aFK48qiYoweGLDmKDyWiNxoxGGA4GwDGoD34DiDDPfD03Db4D+fjiD72r32WT5rePDQ4GyDiUti2rDm4DfDDd5qgD4CWroDDtDAuW1PPDADAfYoDDlYiO4mODKoDGWClR/iPptBahdZRrDjkPD/R40Xay05vGtXWkXGKoe=cWDtqD976RVSGIYVQu4htNhDoRYemid=weFio3AxqD4cbxKAGNnDIxYFAGqgt+EOeDNwrdGmexuGpWdhGi0kz9urBTsmAe0AxRotcme3DxRA+F0DqY45O5ZB5xrw3YhiD4bQvFiNeiDD; ssxmod_itna2=GqGx9DyDuiiQYY5i7=YAKG7qDOG2ikDnQDBP01DphxQyK08D6DYqGdcFkmNhY=eF34oqrYqSYxD3beY6jETIQkAw7Xsx58ixWUeeD; token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjFkZjgzMTlkLTQ3ZTgtNDg1OS1iNzdmLTgxMDU4NGZhZWMwZiIsImV4cCI6MTc0OTY5NzM0NH0.GCGFR6NI85QMmXtG6OjLxY0F1fze0XPdVBPRNzpTSh8; atpsida=005f535629829fb74bf7df34_1747105344_2; SERVERID=b98d56744b338696d8c13af9b400879a|1747105345|1747105336; tfstk=gU3jT81sNtXjChzO1oAzO4XwlWUsCQ8UG1NttfQV6rUYC1MZ9lFVuImsVAkbHAGMi5etBAwZ038Eij4gWpJ68el0KkXUGqaOQbU0_VCz46LEijf-qfnsie5_VBg_DRHTDuB8_8FY6PHTyaF_eZFOk5BJN82RkOeT6zE8_SIYBAU9Nbeu6PFxBodSwvQRL5OahjO0-hpQzn2jM8_OW4LgcJGAbNQtPSZj9jeSabu7GowLq1p3hqHr6qu0qp6LSboICcHMYg4j6mUYTjYfPyhx4VNr7CS_Hx0Sw4E5TwzsDxiTx0ThxoE_h03YV1QtZPi8vcMXhi2x0YZ3wuC6-7kUej0xVCX-Mvybk7EyJBG8X0u0xqJRlynngrotIHSulfnYPg5c8JNA2G17xNN7LQO5jGA7LQ7uzuiwwoFuGzOWNta8D7V7LQO5jGqYZ7SWNQ6by; isg=BIqKadcq1ueGLlW0I70ajFfB23Asew7VpeYBkBTDNl1oxyqB_Ate5dAx1zMbA4Zt
31 | ```
32 | ## 🚀 功能特性
33 |
34 | - **图片 OCR**:使用 QwenLM 强大的 OCR 功能从图片中提取文字。
35 | - **拖拽上传**:直接将图片拖拽到页面即可识别。
36 | - **复制粘贴**:支持从剪贴板直接粘贴图片进行识别。
37 | - **Token 管理**:支持多 Token 轮询使用,提升稳定性。
38 | - **历史记录**:保存每次识别的结果和图片,方便查看。
39 | - **一键复制**:轻松复制识别结果到剪贴板。
40 | - **数学公式识别**:特别优化了对数学公式的提取,支持 LaTeX 格式输出。
41 | - **API 支持**:提供 `curl` 接口调用,支持通过图片文件、base64 和图片 URL 3种方式。(Apifox调用文档示例(**仅作为代码示例,这个网页调试有问题**):https://0vkh6v4ad8.apifox.cn/)
42 | - **验证码识别**:新增验证码识别功能,支持常见类型的验证码(如数字、字母、混合字符等),提升自动化处理能力。
43 | - **自定义prompt**: 在高级模式下(v1.1.0支持),用户可以自定义 prompt,跳过格式化处理,直接返回原始结果,而在普通模式下,使用默认的 prompt 并保持现有的格式化处理逻辑。
44 | ## qwen模型接口:
45 | https://chat.qwenlm.ai/api/models
46 | ## 提示词工程
47 | ```
48 | const defaultPrompt =
49 | '不要输出任何额外的解释或说明,禁止输出例如:识别内容、以上内容已严格按照要求进行格式化和转换等相关无意义的文字!' + '请识别图片中的内容,注意以下要求:\n' +
50 | '对于数学公式和普通文本:\n' +
51 | '1. 所有数学公式和数学符号都必须使用标准的LaTeX格式\n' +
52 | '2. 行内公式使用单个$符号包裹,如:$x^2$\n' +
53 | '3. 独立公式块使用两个$$符号包裹,如:$$\\sum_{i=1}^n i^2$$\n' +
54 | '4. 普通文本保持原样,不要使用LaTeX格式\n' +
55 | '5. 保持原文的段落格式和换行\n' +
56 | '6. 明显的换行使用\\n表示\n' +
57 | '7. 确保所有数学符号都被正确包裹在$或$$中\n\n' +
58 | '对于验证码图片:\n' +
59 | '1. 只输出验证码字符,不要加任何额外解释\n' +
60 | '2. 忽略干扰线和噪点\n' +
61 | '3. 注意区分相似字符,如0和O、1和l、2和Z等\n' +
62 | '4. 验证码通常为4-6位字母数字组合\n\n' +
63 | '';
64 | ```
65 | ## 🛠️ 部署指南
66 |
67 | ### 1. 部署到 Cloudflare Workers
68 |
69 | 1. **配置 Cloudflare Workers**:
70 | - 登录 [Cloudflare Dashboard](https://dash.cloudflare.com/)。
71 | - 创建一个新的 Worker。
72 | - 将 `worker.js` 中的代码复制到 Worker 编辑器中。
73 |
74 | 2. **部署**:
75 | - 保存并部署 Worker。
76 | - 获取 Worker 的访问地址,即可使用。
77 | ### 2. Docker 一键部署
78 | 1. 使用以下命令拉取并运行Docker镜像。
79 | ```
80 | docker pull sexgirls/qwen-ocr-app:latest
81 | docker run -p 3000:3000 sexgirls/qwen-ocr-app:latest
82 | ```
83 | 2. 然后在浏览器中访问应用:
84 | ```
85 | http://localhost:3000
86 | ```
87 | 详情可见:https://github.com/Cunninger/ocr-based-qwen/tree/docker-version
88 | ## 🧩 使用说明
89 |
90 | 1. **设置 Cookie**:
91 | - 前往 [QwenLM](https://chat.qwenlm.ai/) 获取对话请求中的 Cookie。
92 | 
93 |
94 | - 点击右上角的 **⚙️ Cookie设置** 按钮。
95 | - 输入你的 Cookie(或者使用测试Cookie)。
96 | - 点击 **保存**。
97 |
98 | 2. **上传图片**:
99 | - 拖拽图片到页面,或点击上传区域选择图片。
100 | - 支持直接粘贴图片。
101 |
102 | 3. **查看结果**:
103 | - 识别结果会显示在页面下方。
104 | - 点击 **复制结果** 按钮,将识别内容复制到剪贴板。
105 |
106 | 4. **查看历史记录**:
107 | - 点击左侧的 **📋 识别历史** 按钮,查看历史识别记录。
108 | - 点击历史记录中的图片,可以查看大图。
109 |
110 | 5. **API 调用**:
111 | - **支持 base64**:
112 | ```bash
113 | curl -X POST \
114 | 'https://test-qwen-cor.aughumes8.workers.dev/api/recognize/base64' \
115 | -H 'Content-Type: application/json' \
116 | -H 'x-custom-cookie: YOUR_COOKIE_STRING' \
117 | -d '{
118 | "base64Image": "YOUR_BASE64_IMAGE_STRING"
119 | }'
120 | ```
121 | - **支持图片 URL**:
122 | ```bash
123 | curl -X POST \
124 | 'https://test-qwen-cor.aughumes8.workers.dev/api/recognize/url' \
125 | -H 'Content-Type: application/json' \
126 | -H 'x-custom-cookie: YOUR_COOKIE_STRING' \
127 | -d '{
128 | "imageUrl": "YOUR_IMAGE_URL"
129 | }'
130 | ```
131 | 6. **验证码识别**
132 | 
133 |
134 |
135 | ## 📜 许可证
136 |
137 | 本项目基于 MIT 许可证开源。详情请查看 [LICENSE](LICENSE) 文件。
138 |
139 | ## 🙏 致谢
140 |
141 | - 感谢 [QwenLM](https://chat.qwenlm.ai/) 提供的 OCR 功能。
142 | - 感谢 Cloudflare 提供的 Workers 服务。
143 |
144 | ---
145 |
146 | 🌟 如果觉得这个项目对你有帮助,欢迎点个 Star 支持一下!🌟
147 |
148 | **体验地址**:[智能图片识别 (doublefenzhuan.me)](https://ocr.doublefenzhuan.me/)
149 |
150 | **GitHub 仓库**:[Cunninger/ocr-based-qwen](https://github.com/Cunninger/ocr-based-qwen)
151 |
152 | ---
153 |
154 | #### 后续计划
155 | - 优化数学公式识别精度;
156 | - 增加更多 API 功能支持;
157 | - 提升识别速度和稳定性。
158 |
159 | 快来体验吧!如果有任何问题或建议,欢迎在 GitHub 上提 Issue 或直接联系我!
160 |
161 | ## 更新
162 | ### 2025/01/13 应佬友需求,优化了对数学公式的识别,效果如下图
163 | - 原图:
164 |
165 | 
166 |
167 | - 识别效果图:
168 | 
169 |
170 |
171 | ### 2025/01/13 18点34分 支持`curl`接口调用
172 | - **支持base64**:
173 | ```bash
174 | curl -X POST \
175 | 'https://test-qwen-cor.aughumes8.workers.dev/api/recognize/base64' \
176 | -H 'Content-Type: application/json' \
177 | -H 'x-custom-cookie: YOUR_COOKIE_STRING' \
178 | -d '{
179 | "base64Image": "YOUR_BASE64_IMAGE_STRING"
180 | }'
181 | ```
182 | - 效果图:
183 | 
184 |
185 | - **支持图片URL**:
186 | ```bash
187 | curl -X POST \
188 | 'https://test-qwen-cor.aughumes8.workers.dev/api/recognize/url' \
189 | -H 'Content-Type: application/json' \
190 | -H 'x-custom-cookie: YOUR_COOKIE_STRING' \
191 | -d '{
192 | "imageUrl": "YOUR_IMAGE_URL"
193 | }'
194 | ```
195 | - 效果图:
196 | 
197 |
198 | - **通过图片文件识别(需要先上传获取imageId)**
199 | ```bash
200 | # 1. 先上传文件
201 | curl -X POST \
202 | 'https://test-qwen-cor.aughumes8.workers.dev/proxy/upload' \
203 | -H 'x-custom-cookie: YOUR_COOKIE_STRING' \
204 | -F 'file=@/path/to/your/image.jpg'
205 |
206 | # 2. 使用返回的imageId进行识别
207 | curl -X POST \
208 | 'https://test-qwen-cor.aughumes8.workers.dev/recognize' \
209 | -H 'Content-Type: application/json' \
210 | -H 'x-custom-cookie: YOUR_COOKIE_STRING' \
211 | -d '{
212 | "imageId": "RETURNED_IMAGE_ID"
213 | }'
214 | ```
215 | ## Cloudflare访问数据
216 | 
217 | ## 致谢
218 | 
219 | ## 趋势
220 | [](https://star-history.com/#Cunninger/ocr-based-qwen&Date)
221 |
--------------------------------------------------------------------------------
/worker.js:
--------------------------------------------------------------------------------
1 | addEventListener('fetch', event => {
2 | event.respondWith(handleRequest(event.request));
3 | });
4 |
5 | async function handleRequest(request) {
6 | const url = new URL(request.url);
7 | const cookie = request.headers.get('x-custom-cookie') || ''; // 从自定义请求头获取 cookie
8 |
9 | // 处理 CORS 预检请求
10 | if (request.method === 'OPTIONS') {
11 | return new Response(null, {
12 | headers: {
13 | 'Access-Control-Allow-Origin': '*',
14 | 'Access-Control-Allow-Methods': 'POST, OPTIONS',
15 | 'Access-Control-Allow-Headers': 'Content-Type, Authorization, x-custom-cookie', // 添加自定义请求头
16 | },
17 | });
18 | }
19 |
20 | // API路由处理
21 | switch (url.pathname) {
22 | // 1. 通过图片URL识别
23 | case '/api/recognize/url':
24 | if (request.method === 'POST') {
25 | return handleImageUrlRecognition(request);
26 | }
27 | break;
28 |
29 | // 2. 通过Base64识别
30 | case '/api/recognize/base64':
31 | if (request.method === 'POST') {
32 | return handleBase64Recognition(request);
33 | }
34 | break;
35 |
36 | // 3. 通过图片文件识别 (原有的/recognize端点)
37 | case '/recognize':
38 | if (request.method === 'POST') {
39 | return handleFileRecognition(request);
40 | }
41 | break;
42 |
43 | // 添加代理路由
44 | case '/proxy/upload':
45 | if (request.method === 'POST') {
46 | return handleProxyUpload(request);
47 | }
48 | break;
49 |
50 | // 返回前端界面
51 | case '/':
52 | return new Response(getHTML(), {
53 | headers: { 'Content-Type': 'text/html' },
54 | });
55 | }
56 |
57 | return new Response('Not Found', { status: 404 });
58 | }
59 |
60 | // 处理图片URL识别
61 | async function handleImageUrlRecognition(request) {
62 | try {
63 | const { imageUrl } = await request.json();
64 | const cookie = request.headers.get('x-custom-cookie');
65 |
66 | if (!cookie || !imageUrl) {
67 | return new Response(JSON.stringify({
68 | error: 'Missing cookie or imageUrl'
69 | }), {
70 | status: 400,
71 | headers: { 'Content-Type': 'application/json' },
72 | });
73 | }
74 |
75 | // 从cookie中提取token
76 | const tokenMatch = cookie.match(/token=([^;]+)/);
77 | if (!tokenMatch) {
78 | return new Response(JSON.stringify({
79 | error: 'Invalid cookie format: missing token'
80 | }), {
81 | status: 400,
82 | headers: { 'Content-Type': 'application/json' },
83 | });
84 | }
85 | const token = tokenMatch[1];
86 |
87 | // 下载图片
88 | const imageResponse = await fetch(imageUrl);
89 | const imageBlob = await imageResponse.blob();
90 |
91 | // 上传到QwenLM
92 | const formData = new FormData();
93 | formData.append('file', imageBlob);
94 |
95 | const uploadResponse = await fetch('https://chat.qwenlm.ai/api/v1/files/', {
96 | method: 'POST',
97 | headers: {
98 | 'accept': 'application/json',
99 | 'authorization': `Bearer ${token}`,
100 | 'cookie': cookie
101 | },
102 | body: formData,
103 | });
104 |
105 | const uploadData = await uploadResponse.json();
106 | if (!uploadData.id) throw new Error('File upload failed');
107 |
108 | // 调用通用识别函数
109 | return await recognizeImage(token, uploadData.id, request);
110 | } catch (error) {
111 | return new Response(JSON.stringify({
112 | error: error.message || 'Internal Server Error'
113 | }), {
114 | status: 500,
115 | headers: { 'Content-Type': 'application/json' },
116 | });
117 | }
118 | }
119 |
120 | // 处理Base64识别
121 | async function handleBase64Recognition(request) {
122 | try {
123 | const { base64Image } = await request.json();
124 | const cookie = request.headers.get('x-custom-cookie');
125 |
126 | if (!cookie || !base64Image) {
127 | return new Response(JSON.stringify({
128 | error: 'Missing cookie or base64Image'
129 | }), {
130 | status: 400,
131 | headers: { 'Content-Type': 'application/json' },
132 | });
133 | }
134 |
135 | // 从cookie中提取token
136 | const tokenMatch = cookie.match(/token=([^;]+)/);
137 | if (!tokenMatch) {
138 | return new Response(JSON.stringify({
139 | error: 'Invalid cookie format: missing token'
140 | }), {
141 | status: 400,
142 | headers: { 'Content-Type': 'application/json' },
143 | });
144 | }
145 | const token = tokenMatch[1];
146 |
147 | // 转换Base64为Blob
148 | const imageData = base64Image.startsWith('data:') ? base64Image : 'data:image/png;base64,' + base64Image;
149 | const response = await fetch(imageData);
150 | const blob = await response.blob();
151 |
152 | // 上传到QwenLM
153 | const formData = new FormData();
154 | formData.append('file', blob);
155 |
156 | const uploadResponse = await fetch('https://chat.qwenlm.ai/api/v1/files/', {
157 | method: 'POST',
158 | headers: {
159 | 'accept': 'application/json',
160 | 'authorization': `Bearer ${token}`,
161 | 'cookie': cookie
162 | },
163 | body: formData,
164 | });
165 |
166 | const uploadData = await uploadResponse.json();
167 | if (!uploadData.id) throw new Error('File upload failed');
168 |
169 | // 调用通用识别函数
170 | return await recognizeImage(token, uploadData.id, request);
171 | } catch (error) {
172 | return new Response(JSON.stringify({
173 | error: error.message || 'Internal Server Error'
174 | }), {
175 | status: 500,
176 | headers: { 'Content-Type': 'application/json' },
177 | });
178 | }
179 | }
180 |
181 | // 处理文件识别 (原有功能)
182 | async function handleFileRecognition(request) {
183 | try {
184 | const { imageId } = await request.json();
185 | const cookie = request.headers.get('x-custom-cookie') || '';
186 |
187 | if (!cookie || !imageId) {
188 | return new Response(JSON.stringify({
189 | error: 'Missing cookie or imageId'
190 | }), {
191 | status: 400,
192 | headers: { 'Content-Type': 'application/json' },
193 | });
194 | }
195 |
196 | // 从cookie中提取token
197 | const tokenMatch = cookie.match(/token=([^;]+)/);
198 | const token = tokenMatch ? tokenMatch[1] : '';
199 |
200 | return await recognizeImage(token, imageId, request);
201 | } catch (error) {
202 | return new Response(JSON.stringify({
203 | error: error.message || 'Internal Server Error'
204 | }), {
205 | status: 500,
206 | headers: { 'Content-Type': 'application/json' },
207 | });
208 | }
209 | }
210 |
211 | // 添加代理处理函数
212 | async function handleProxyUpload(request) {
213 | try {
214 | const formData = await request.formData();
215 | const cookie = request.headers.get('x-custom-cookie') || '';
216 |
217 | // 从cookie中提取token
218 | const tokenMatch = cookie.match(/token=([^;]+)/);
219 | const token = tokenMatch ? tokenMatch[1] : '';
220 |
221 | const response = await fetch('https://chat.qwenlm.ai/api/v1/files/', {
222 | method: 'POST',
223 | headers: {
224 | 'accept': 'application/json',
225 | 'authorization': `Bearer ${token}`,
226 | 'cookie': cookie
227 | },
228 | body: formData,
229 | });
230 |
231 | const data = await response.json();
232 |
233 | return new Response(JSON.stringify(data), {
234 | headers: {
235 | 'Content-Type': 'application/json',
236 | 'Access-Control-Allow-Origin': '*',
237 | },
238 | });
239 | } catch (error) {
240 | return new Response(JSON.stringify({
241 | error: error.message || 'Proxy upload failed'
242 | }), {
243 | status: 500,
244 | headers: {
245 | 'Content-Type': 'application/json',
246 | 'Access-Control-Allow-Origin': '*',
247 | },
248 | });
249 | }
250 | }
251 |
252 | // 通用的识别函数
253 | async function recognizeImage(token, imageId, request) {
254 | const cookie = request.headers.get('x-custom-cookie') || '';
255 |
256 | // 从请求头中获取高级模式状态和自定义prompt
257 | const advancedMode = request.headers.get('x-advanced-mode') === 'true';
258 |
259 | // 解码自定义prompt
260 | let customPrompt = '';
261 | try {
262 | const encodedPrompt = request.headers.get('x-custom-prompt');
263 | if (encodedPrompt) {
264 | customPrompt = decodeURIComponent(atob(encodedPrompt));
265 | }
266 | } catch (error) {
267 | console.error('Prompt解码错误:', error);
268 | }
269 |
270 | const defaultPrompt =
271 | '不要输出任何额外的解释或说明,禁止输出例如:识别内容、以上内容已严格按照要求进行格式化和转换等相关无意义的文字!' + '请识别图片中的内容,注意以下要求:\n' +
272 | '对于数学公式和普通文本:\n' +
273 | '1. 所有数学公式和数学符号都必须使用标准的LaTeX格式\n' +
274 | '2. 行内公式使用单个$符号包裹,如:$x^2$\n' +
275 | '3. 独立公式块使用两个$$符号包裹,如:$$\\sum_{i=1}^n i^2$$\n' +
276 | '4. 普通文本保持原样,不要使用LaTeX格式\n' +
277 | '5. 保持原文的段落格式和换行\n' +
278 | '6. 明显的换行使用\\n表示\n' +
279 | '7. 确保所有数学符号都被正确包裹在$或$$中\n\n' +
280 | '对于验证码图片:\n' +
281 | '1. 只输出验证码字符,不要加任何额外解释\n' +
282 | '2. 忽略干扰线和噪点\n' +
283 | '3. 注意区分相似字符,如0和O、1和l、2和Z等\n' +
284 | '4. 验证码通常为4-6位字母数字组合\n\n' +
285 | '';
286 | const response = await fetch('https://chat.qwenlm.ai/api/chat/completions', {
287 | method: 'POST',
288 | headers: {
289 | 'Content-Type': 'application/json',
290 | 'User-Agent': 'PostmanRuntime/7.43.0',
291 | 'accept': '*/*',
292 | 'authorization': `Bearer ${token}`,
293 | 'cookie': cookie,
294 | },
295 | body: JSON.stringify({
296 | stream: false,
297 | chat_type: "t2t",
298 | model: 'qwen-max-latest',
299 | messages: [
300 | {
301 | role: 'user',
302 | content: [
303 | {
304 | type: 'text',
305 | text: advancedMode ? customPrompt : defaultPrompt,
306 | chat_type: "t2t"
307 | },
308 | {
309 | type: 'image',
310 | image: imageId,
311 | chat_type: "t2t"
312 | },
313 | ],
314 | },
315 | ],
316 | session_id: '1',
317 | chat_id: '2',
318 | id: '3',
319 | }),
320 | });
321 |
322 | const data = await response.json();
323 | let result = data.choices[0]?.message?.content || '识别失败';
324 |
325 | // 只在非高级模式下进行格式化处理
326 | if (!advancedMode) {
327 | // 如果结果长度小于10且只包含字母数字,很可能是验证码
328 | if (result.length <= 10 && /^[A-Za-z0-9]+$/.test(result)) {
329 | return new Response(JSON.stringify({
330 | success: true,
331 | result: result.toUpperCase(),
332 | type: 'captcha'
333 | }), {
334 | headers: {
335 | 'Content-Type': 'application/json',
336 | 'Access-Control-Allow-Origin': '*',
337 | },
338 | });
339 | }
340 |
341 | // 其他情况(数学公式和普通文本)的处理
342 | result = result
343 | .replace(/\\(/g, '\\(')
344 | .replace(/\\)/g, '\\)')
345 | .replace(/\n{3,}/g, '\n\n')
346 | .replace(/([^\n])\n([^\n])/g, '$1\n$2')
347 | .replace(/\$\s+/g, '$')
348 | .replace(/\s+\$/g, '$')
349 | .replace(/\$\$/g, '$$')
350 | .trim();
351 | }
352 |
353 | return new Response(JSON.stringify({
354 | success: true,
355 | result: result,
356 | type: 'text'
357 | }), {
358 | headers: {
359 | 'Content-Type': 'application/json',
360 | 'Access-Control-Allow-Origin': '*',
361 | },
362 | });
363 | }
364 |
365 | function getHTML() {
366 | const html = [
367 | '',
368 | '',
369 | '',
370 | ' ',
371 | ' ',
372 | ' ',
373 | 'Qwen VL 智能识别系统 ',
374 |
375 | // 添加 MathJax 支持
376 | '',
377 | '',
398 | '',
399 | '',
413 |
414 | '',
1267 | '',
1268 | '',
1269 | '',
1270 | ' 获取Cookie',
1271 | ' ',
1272 | '',
1273 | ' ',
1274 | ' ',
1275 | ' ',
1276 | ' ',
1277 | '',
1278 | '',
1305 |
1306 | '',
1307 | '
Qwen VL 智能识别系统 ',
1308 | '
基于通义千问大模型的多模态智能识别引擎
',
1309 | '
',
1310 | '
📸 ',
1311 | '
',
1312 | '拖拽图片到这里,点击上传,或粘贴图片/Base64/URL ',
1313 | '支持多种输入方式',
1314 | '
',
1315 | '
',
1323 | '
',
1324 | '
',
1325 | '
',
1326 | '
',
1327 | '',
1331 | '
',
1332 | '
',
1333 | '
📋 识别历史 ',
1334 | '',
1341 | '',
1346 | '
',
1347 |
1348 | '',
1349 | '
',
1350 | '
',
1351 |
1352 | '',
2089 | '',
2090 | ''
2091 | ].join('\n');
2092 |
2093 | return html;
2094 | }
--------------------------------------------------------------------------------