通过Axios发送PDF至Actix Web遇解析错误,求解决方案
问题描述
我用NextJS开发时遇到PDF上传解析问题,流程及代码如下:
1. NextJS前端调用内部API
... mutateUser( await fetchJson("/path/to/api", { method: "POST", headers: { "Content-Type": "application/pdf" }, body: file, // const [file, setFile] = useState<File | null>(null); }) ); ...
2. Next内部API通过Axios调用Actix Web服务器
... const { token } = req.session.user; const body = req.body; const api_url = `${process.env.SERVER_API_URL}/path/to/api` as string; console.log(body.length); try { const response = await axios.post(api_url, body, { headers: { Authorization: `Bearer ${token}`, "Content-Type": "application/pdf", }, }); res.json({ ...response.data, isLoggedIn: true }); } ...
3. Actix Web端处理逻辑
pub async fn route_function(db: Data<DatabaseRepository>, mut payload: Payload, auth: AuthorizationService) -> HttpResponse { let id = auth.id; let mut bytes = BytesMut::new(); while let Some(item) = payload.next().await { bytes.extend_from_slice(&item.unwrap()); } log::debug!("bytes: {:?}", bytes); log::debug!("bytes length: {:?}", bytes.len()); let pdf_text = match pdf_extract::extract_text_from_mem(&bytes) { Ok(text) => text, Err(e) => { // PDF parsing error only with Axios log::error!("Error: {:#?}", e); return HttpResponse::BadRequest().json(ErrorResponse::new("error parsing pdf".to_string(), e.to_string())); } }; ...
核心问题
用Postman或cURL直接调用Rust路由完全正常,参考命令:
# This works curl -X POST -H "Content-Type: application/pdf" -H "Authorization: Bearer <TOKEN>" --data-binary "@/path/to/file.pdf" http://localhost:8080/path/to/api
但通过Axios转发请求时,PDF解析库提示解析PDF Trailer出错。对比字节数发现:Axios转发的请求约101k字节,而Postman/cURL的请求是58k字节,推测字节数异常是问题根源。尝试过用FormData也没解决,希望能调整Actix路由实现兼容,目前只知道用Payload提取器读取PDF的方式。
解决方案
方案1:修复Next API中请求体的处理方式
Next.js的API路由默认会自动解析请求体,导致原始PDF二进制数据被转成字符串或其他格式,转发给Axios时数据损坏。需要禁用自动解析,直接读取原始Buffer:
// 禁用Next的自动解析配置 export const config = { api: { bodyParser: false, }, }; export default async function handler(req, res) { const { token } = req.session.user; const api_url = `${process.env.SERVER_API_URL}/path/to/api` as string; // 读取原始二进制数据 const chunks = []; for await (const chunk of req) { chunks.push(chunk); } const body = Buffer.concat(chunks); try { const response = await axios.post(api_url, body, { headers: { Authorization: `Bearer ${token}`, "Content-Type": "application/pdf", }, }); res.json({ ...response.data, isLoggedIn: true }); } catch (err) { res.status(500).json({ error: err.message }); } }
方案2:调整Axios的请求配置
确保Axios发送原始二进制数据,避免自动序列化,添加transformRequest配置阻止请求体转换:
const response = await axios.post(api_url, body, { headers: { Authorization: `Bearer ${token}`, "Content-Type": "application/pdf", }, transformRequest: [(data) => data], // 阻止Axios修改请求体 });
方案3:Actix端兼容处理(无法修改前端/Next API时)
如果只能从Actix端调整,可尝试检查请求体是否被编码(如base64)并解码:
pub async fn route_function(db: Data<DatabaseRepository>, mut payload: Payload, auth: AuthorizationService) -> HttpResponse { let id = auth.id; let mut bytes = BytesMut::new(); while let Some(item) = payload.next().await { bytes.extend_from_slice(&item.unwrap()); } log::debug!("bytes length: {:?}", bytes.len()); // 尝试解码base64(字节数膨胀符合base64编码特征) let pdf_bytes = match base64::decode(&bytes) { Ok(decoded) => { log::debug!("Decoded base64 to bytes length: {}", decoded.len()); decoded }, Err(_) => bytes.to_vec(), // 非base64则用原始字节 }; let pdf_text = match pdf_extract::extract_text_from_mem(&pdf_bytes) { Ok(text) => text, Err(e) => { log::error!("Error: {:#?}", e); return HttpResponse::BadRequest().json(ErrorResponse::new("error parsing pdf".to_string(), e.to_string())); } }; // 后续业务逻辑... }
说明
字节数从58k膨胀到101k,大概率是Next自动将二进制转成UTF-8字符串导致的,方案1是最根本的解决方式——确保请求体在转发过程中保持原始二进制格式,未被任何解析或转换。
内容的提问来源于stack exchange,提问作者Santiago Medina
相关产品推荐
相关产品推荐

