如何在PHP中通过CSRF令牌与Basic认证向Paperless NGX API上传文件
解决Paperless NGX文件上传的CSRF令牌获取及请求问题
问题根源
你当前使用/api/documents/post_document/这个仅接受POST请求的端点去发起GET请求获取CSRF令牌,因此触发了405 Method Not Allowed错误。Paperless NGX提供了专门的CSRF令牌获取方式,无需使用上传端点。
修正步骤
1. 正确获取CSRF令牌
Paperless NGX的API提供了专门的/api/csrf/端点用于获取CSRF令牌,该端点接受GET请求,直接返回包含令牌的JSON响应,同时响应头也会携带X-CSRFToken字段。
修正后的令牌获取函数:
function getCsrfToken($baseUrl, $username, $password) { $csrfUrl = rtrim($baseUrl, '/') . '/api/csrf/'; $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $csrfUrl); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_USERPWD, "$username:$password"); curl_setopt($ch, CURLOPT_HEADER, true); $response = curl_exec($ch); $headerSize = curl_getinfo($ch, CURLINFO_HEADER_SIZE); $body = substr($response, $headerSize); curl_close($ch); // 优先从响应体提取令牌(更直接) $bodyData = json_decode($body, true); if (!empty($bodyData['csrfToken'])) { return $bodyData['csrfToken']; } // 备用:从响应头提取 foreach (explode("\r\n", substr($response, 0, $headerSize)) as $line) { if (strpos($line, 'X-CSRFToken:') !== false) { list($key, $token) = explode(': ', $line); return trim($token); } } return null; }
调用时传入Paperless的基础URL(而非上传端点):
$baseUrl = 'http://hostname:8000'; $csrfToken = getCsrfToken($baseUrl, $username, $password); if (!$csrfToken) { die('CSRF-Token could not be retrieved'); }
2. 修正文件上传代码
上传部分存在两个关键问题:
- 手动设置
Content-Type: multipart/form-data会覆盖CURL自动生成的表单边界标识,导致Paperless无法解析上传数据,需移除该头信息。 - 建议显式指定文件的MIME类型,避免自动检测错误。
修正后的上传函数:
function uploadFile($apiUrl, $filePath, $csrfToken, $username, $password) { $ch = curl_init(); // 显式指定PDF的MIME类型,提升兼容性 $file = new CURLFile($filePath, 'application/pdf', basename($filePath)); $postData = [ 'document' => $file // 可选:添加Paperless支持的其他参数,比如文档标题、标签ID等 // 'title' => 'Invoice 2024', // 'tags' => [1, 4] ]; curl_setopt($ch, CURLOPT_URL, $apiUrl); curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_POSTFIELDS, $postData); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_USERPWD, "$username:$password"); curl_setopt($ch, CURLOPT_HTTPHEADER, [ 'X-CSRFToken: ' . $csrfToken, ]); $response = curl_exec($ch); $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); // 调试用:失败时输出详细信息 if ($httpCode !== 201) { echo 'HTTP状态码: ' . $httpCode . PHP_EOL; echo '响应内容: ' . $response . PHP_EOL; echo 'CURL错误: ' . curl_error($ch) . PHP_EOL; } curl_close($ch); if ($httpCode == 201) { echo 'File uploaded successfully!'; } else { echo 'Error uploading file.'; } }
调用时传入正确的上传端点:
$apiUrl = rtrim($baseUrl, '/') . '/api/documents/post_document/'; uploadFile($apiUrl, $filePath, $csrfToken, $username, $password);
额外注意事项
- 确保你的Paperless NGX实例启用了Basic认证,且当前用户拥有文档上传权限。
- 若仍有问题,可查看Paperless的日志文件(通常位于
/opt/paperless-ngx/logs/或容器日志),定位具体错误原因。 - 确保PHP版本在5.5以上(支持
CURLFile),推荐使用PHP 7.4及以上版本。
内容的提问来源于stack exchange,提问作者jlai
相关产品推荐
相关产品推荐

