客户端post请求携带数据过多致使无法上传成功并报400异常

App端上传图片,图片数量多会超时。用wireshark拦截数据包,发现因网络重传,请求头content-length值大。对比成功与失败数据包,除长度外其他一致。修改Tomcat中server.xml里connector标签的postMaxsize值,解决了上传问题。

app端会执行上传图片的操作,是将图片转换为base码后传输,但是发现图片数量较多时,一直出现超时操作。

使用wireshark拦截接收到的数据包时,发现由于网络原因出现的重传,请求头中的content-length的值为:2674178,单位为字节,这样算下来的话,内容在2.55M左右。

拦截提交成功的数据包,显示content-length的值为:987463,大约在0.94M左右。而且同事对比发现,两者除了length长度不一样之外,其它的部分是一致的。于是修改了tomcat中server.xml中connector标签中的postMaxsize值的大小

Tomcat 默认的post参数的最大大小为2M, 当超过时将会出错,可以配置maxPostSize参数来改变大小。

从 apache-tomcat-7.0.63 开始,参数 maxPostSize 的含义就变了: 如果将值设置为 0,表示 POST 最大值为 0,不限制 POST 大小需要将值设置为 -1。,在此版本之前设置为 0 表示不限制 POST 大小。

设置完成以后,发现可以成功上传了。

记录下来,以便后续出现相同问题的时候可以查阅,共同学习,共同进步。

 

import { defineEventHandler, getRequestIP, getQuery, readBody, createError, readMultipartFormData, } from "h3"; import { useRuntimeConfig } from "#imports"; import { checkRateLimit } from "~/server/limit/limiter"; import { ALIBABA_MODE_BASE_URL, ALIBABA_APP_BASE_URL, ALIBABA_UPLOAD_BASE_URL, ALIBABA_IMAGE_BASE_URL, Alibaba, } from "~/server/constant"; import { ApiPath } from "~/utils/constant"; export default defineEventHandler(async (event) => { console.log(11111111111111); if (event.node.req.method === "OPTIONS") { event.node.res.statusCode = 200; event.node.res.setHeader("Content-Type", "application/json"); event.node.res.end(JSON.stringify({ body: "OK" })); return; } try { const ip = getRequestIP(event); // 验证请求频率 if (!(await checkRateLimit(ip))) { throw createError({ statusCode: 429, message: "请求过于频繁,请稍后再试", }); } const authHeader = event.node.req.headers.authorization; const cookieHeader = event.node.req.headers.cookie; const cookies = typeof cookieHeader === "string" ? cookieHeader : ""; let token = null; if (authHeader && authHeader.startsWith("Bearer ")) { token = authHeader.substring(7); } else if (cookies) { const tokenCookie = cookies .split(";") .find((c) => c.trim().startsWith("auth._token.local=")); if (tokenCookie) { token = decodeURIComponent(tokenCookie.split("=")[1]); } } if (!token) { throw createError({ statusCode: 401, message: "未授权的访问", }); } const config = useRuntimeConfig(); const apiKey = config.alibabaApiKey; if (!apiKey) { throw createError({ statusCode: 500, message: "API 密钥未配置", }); } // 处理文件上传请求 const query = getQuery(event); if (event.node.req.method === "POST" && query.action === "fileExtract") { const formData = await readMultipartFormData(event); if (!formData) { throw createError({ statusCode: 400, message: "无效的表单数据", }); } const fileData = formData.find((part) => part.name === "file"); if (!fileData) { throw createError({ statusCode: 400, message: "未找到文件数据", }); } // 检查文件类型和扩展名 const fileName = fileData.filename || ""; const fileExt = fileName.split(".").pop()?.toLowerCase(); const supportedExtensions = [ "txt", "doc", "docx", "pdf", "xlsx", "epub", "mobi", "md", "csv", ]; if (!supportedExtensions.includes(fileExt)) { throw createError({ statusCode: 400, message: "不支持的文件类型,仅支持 txt、doc、docx、pdf、xlsx、epub、mobi、md、csv 格式", }); } // 检查文件大小(150MB = 150 * 1024 * 1024 bytes) if (fileData.data.length > 150 * 1024 * 1024) { throw createError({ statusCode: 400, message: "文件大小不能超过 150MB", }); } // 创建新的 FormData 对象用于转发 const forwardFormData = new FormData(); forwardFormData.append( "file", new Blob([fileData.data], { type: fileData.type }), fileData.filename ); forwardFormData.append("purpose", "file-extract"); try { // const response = await fetch(`${ALIBABA_MODE_BASE_URL}v1/files`, { // method: "POST", // headers: { // Authorization: `Bearer ${apiKey}`, // }, // body: forwardFormData, // }); // if (!response.ok) { // // 修改错误处理逻辑,避免尝试解析可能不是 JSON 格式的响应 // const contentType = response.headers.get("content-type"); // let errorMessage = `文件上传失败 (${response.status})`; // if (contentType && contentType.includes("application/json")) { // try { // const errorData = await response.json(); // errorMessage = errorData.error?.message || errorMessage; // } catch (parseError) { // console.error("解析错误响应失败:", parseError); // } // } else { // // 如果不是 JSON 格式,尝试获取文本内容 // try { // await response.text(); // } catch (textError) { // // 读取错误响应文本失败 // } // } // throw createError({ // statusCode: response.status, // message: errorMessage, // }); // } // // 确保响应是 JSON 格式 // const contentType = response.headers.get("content-type"); // if (!contentType || !contentType.includes("application/json")) { // const textResponse = await response.text(); // // 尝试手动解析响应 // try { // return JSON.parse(textResponse); // } catch (parseError) { // throw createError({ // statusCode: 500, // message: "服务器返回了非 JSON 格式的响应", // }); // } // } // return await response.json(); } catch (error) { throw createError({ statusCode: 500, message: error.message || "文件上传失败", }); } } // 处理获取上传策略的请求 if (event.node.req.method === "GET" && query.action === "getPolicy") { const modelName = query.model || "qwen-vl-plus"; // 转发请求到阿里云 DashScope const dashscopeUrl = `${ALIBABA_UPLOAD_BASE_URL}?action=getPolicy&model=${modelName}`; try { const response = await fetch(dashscopeUrl, { method: "GET", headers: { Authorization: `Bearer ${apiKey}`, "Content-Type": "application/json", }, }); if (!response.ok) { const errorData = await response.json(); throw createError({ statusCode: response.status, message: errorData.message || `获取上传策略失败 (${response.status})`, }); } const policyData = await response.json(); return policyData; } catch (error) { throw createError({ statusCode: 500, message: error.message || "获取上传策略失败", }); } } // 删除文件 if (event.node.req.method === "DELETE" && query.action === "deleteFile") { const fileId = query.fileId; if (!fileId) { throw createError({ statusCode: 400, message: "缺少文件 ID 参数", }); } try { const response = await fetch( `${ALIBABA_MODE_BASE_URL}v1/files/${fileId}`, { method: "DELETE", headers: { Authorization: `Bearer ${apiKey}`, "Content-Type": "application/json", }, } ); if (!response.ok) { const errorData = await response.json(); throw createError({ statusCode: response.status, message: errorData.error?.message || `删除文件失败 (${response.status})`, }); } const result = await response.json(); return { success: true, ...result }; } catch (error) { throw createError({ statusCode: error.statusCode || 500, message: error.message || "删除文件失败", }); } } const body = await readBody(event); // ============== 新增文生图处理逻辑 ============== if (event.node.req.method === "POST" && query.action === "textToImage") { try { // 验证必要参数 if (!body.prompt) { throw createError({ statusCode: 400, message: "缺少 prompt 参数", }); } // 构造请求参数 const requestBody = { model: body.model || "wanx-lite", input: { prompt: body.prompt, }, parameters: { size: body.size || "1024x1024", n: body.n || 1, }, prompt_extend: true, watermark: false }; try { const response = await fetch(`${ALIBABA_IMAGE_BASE_URL}${Alibaba.TextToImagePath}`, { method: "POST", headers: { "Content-Type": "application/json", Authorization: `Bearer ${apiKey}`, "X-DashScope-Async": "enable" }, body: JSON.stringify(requestBody), }); // 读取原始响应文本 // const responseText = await response.text(); // console.log("Raw API response:", responseText); // 调试日志 // // 处理空响应 // if (!responseText) { // throw createError({ // statusCode: 502, // message: "收到空响应" // }); // } // // 尝试解析 JSON // let result; // try { // result = JSON.parse(responseText); // } catch (e) { // throw createError({ // statusCode: 502, // message: `无效的JSON响应: ${e.message}\n响应内容: ${responseText}` // }); // } // if (!response.ok) { // throw createError({ // statusCode: response.status, // message: result.error?.message || result.message || `文生图请求失败 (${response.status})`, // }); // } // // 检查是否有任务ID // if (!result.output?.task_id) { // throw createError({ // statusCode: 500, // message: '未获取到任务ID' // }); // } return response; } catch (error) { throw createError({ statusCode: error.statusCode || 500, message: error.message || "文生图处理失败", }); } } catch (error) { throw createError({ statusCode: error.statusCode || 500, message: error.message || "文生图处理失败", }); } } // -------------------文生图 let headers = { "Content-Type": "application/json", Authorization: `Bearer ${apiKey}`, }; // 检查是否包含图片 URL if (body.messages) { let hasImageUrl = false; for (let i = 0; i < body.messages.length; i++) { const msg = body.messages[i]; if (msg.content && Array.isArray(msg.content)) { for (const item of msg.content) { if (item.type === "image_url" && item.image_url) { hasImageUrl = true; } } } else if ( msg.content && typeof msg.content === "object" && msg.content.imageUrl ) { // 处理直接包含imageUrl的消息对象 // 转换消息格式以符合API要求 const role = msg.role || "user"; const text = msg.content.text || ""; const imageUrl = msg.content.imageUrl; // 替换原始消息为正确格式 body.messages[i] = { role: role, content: [ { type: "text", text: text }, { type: "image_url", image_url: { url: imageUrl } }, ], }; hasImageUrl = true; } } if (hasImageUrl) { // 确保添加OSS资源解析头 headers["X-DashScope-OssResourceResolve"] = "enable"; // 如果检测到图片URL,强制使用视觉模型 if (!body.useApp && (!body.model || body.model !== "qwen-vl-plus")) { body.model = "qwen-vl-plus"; } } } let fetchUrl = ""; let requestBody = {}; // 判断是否为应用调用 if (body.useApp && body.appId) { fetchUrl = `${ALIBABA_APP_BASE_URL}${body.appId}${Alibaba.AppCompletionPath}`; // 改进应用调用的消息处理 let prompt = ""; if (body.prompt) { prompt = body.prompt; } else if (body.messages && body.messages.length > 0) { const lastMessage = body.messages[body.messages.length - 1]; if (typeof lastMessage.content === "string") { prompt = lastMessage.content; } else if (typeof lastMessage.content === "object" && lastMessage.content.text) { prompt = lastMessage.content.text; } } requestBody = { input: { prompt: prompt || "你好,请问有什么可以帮助你的?" }, parameters: { incremental_output: "true", }, debug: {}, }; // 如果是流式输出,添加相应的头部 if (body.stream === true) { headers["X-DashScope-SSE"] = "enable"; } } else { // 原有的模型调用逻辑 let path = `${event.node.req.url}`.replaceAll(ApiPath.Alibaba, ""); let baseUrl = config.alibabaUrl || ALIBABA_MODE_BASE_URL; if (!baseUrl.startsWith("http")) { baseUrl = `https://${baseUrl}`; } if (baseUrl.endsWith("/")) { baseUrl = baseUrl.slice(0, -1); } fetchUrl = `${baseUrl}${path}${Alibaba.ModeChatPath}`; requestBody = body; } // 发送请求到阿里云 DashScope API const controller = new AbortController(); const timeoutId = setTimeout(() => controller.abort(), 30000); const response = await fetch(fetchUrl, { method: "POST", headers, body: JSON.stringify(requestBody), signal: controller.signal, }); clearTimeout(timeoutId); if (!response.ok) { const errorData = await response.json(); throw createError({ statusCode: response.status, message: errorData.error?.message || errorData.message || `请求失败 (${response.status})`, }); } // 检查响应是否为流式 if (body.stream === true) { // 设置流式响应头 event.node.res.setHeader("Content-Type", "text/event-stream"); event.node.res.setHeader("Cache-Control", "no-cache"); event.node.res.setHeader("Connection", "keep-alive"); if (!response.body) { throw new Error("响应体为空"); } const reader = response.body.getReader(); const decoder = new TextDecoder(); try { while (true) { const { done, value } = await reader.read(); if (done) break; const chunk = decoder.decode(value, { stream: true }); if (event.node.res.writableEnded) { break; } if (body.useApp && body.appId) { // 处理应用返回的 SSE 格式数据 const lines = chunk.split("\n"); for (const line of lines) { if (line.startsWith("data:") && line.trim() !== "data: [DONE]") { try { const data = JSON.parse(line.substring(5).trim()); if (data.output && data.output.text) { // 转换为与模型调用相同的格式 const formattedChunk = `data: ${JSON.stringify({ choices: [ { delta: { content: data.output.text, }, }, ], })}\n\n`; event.node.res.write(formattedChunk); } } catch (e) { event.node.res.write(line + "\n"); } } else if (line.trim() === "data: [DONE]") { event.node.res.write(line + "\n"); } } } else { // 原有模型调用的响应直接转发 event.node.res.write(chunk); } } } catch (streamError) { event.node.res.end( `data: [错误] 流处理中断:${streamError.message}\n\n` ); return; } finally { reader.cancel(); event.node.res.end(); } return; } else { // 非流式响应 const responseData = await response.json(); // 如果是应用调用,转换响应格式以匹配原有的格式 if (body.useApp && body.appId && responseData.output) { return { choices: [ { message: { content: responseData.output.text, }, }, ], }; } return responseData; } } catch (error) { throw createError({ statusCode: error.statusCode || 500, message: error.message || "服务器内部错误", }); } }); 为什么配置了文生图端点,但是请求一直{ "error": true, "url": "http://192.168.101.199:2000/api/alibaba?action=textToImage", "statusCode": 500, "statusMessage": "Server Error", "message": "Unexpected end of JSON input" },是需要配置什么吗,还是写法有问题,postman里面调用没问题
最新发布
05-23
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值