v1.0.148: PDF parsing via PyPDF2 on VM (replaces broken PDFBox on Lucee)
This commit is contained in:
@@ -57,6 +57,13 @@ class Handler(BaseHTTPRequestHandler):
|
|||||||
self.send_header("Access-Control-Allow-Headers", "Content-Type")
|
self.send_header("Access-Control-Allow-Headers", "Content-Type")
|
||||||
self.end_headers()
|
self.end_headers()
|
||||||
|
|
||||||
|
def do_GET(self):
|
||||||
|
parsed = urlparse(self.path)
|
||||||
|
if parsed.path == "/process-v2":
|
||||||
|
self._handle_process_v2(parsed)
|
||||||
|
else:
|
||||||
|
self.send_error(404)
|
||||||
|
|
||||||
def do_POST(self):
|
def do_POST(self):
|
||||||
if self.path == "/llm-ops":
|
if self.path == "/llm-ops":
|
||||||
self._handle_llm_ops()
|
self._handle_llm_ops()
|
||||||
@@ -64,6 +71,8 @@ class Handler(BaseHTTPRequestHandler):
|
|||||||
self._handle_unzip_upload()
|
self._handle_unzip_upload()
|
||||||
elif self.path == "/upload":
|
elif self.path == "/upload":
|
||||||
self._handle_upload()
|
self._handle_upload()
|
||||||
|
elif self.path == "/parse-pdf":
|
||||||
|
self._handle_parse_pdf()
|
||||||
else:
|
else:
|
||||||
self._handle_doc_convert()
|
self._handle_doc_convert()
|
||||||
|
|
||||||
@@ -345,6 +354,43 @@ class Handler(BaseHTTPRequestHandler):
|
|||||||
else:
|
else:
|
||||||
self._send_error_cors(502, f"assemble failed: HTTP {resp.status_code}")
|
self._send_error_cors(502, f"assemble failed: HTTP {resp.status_code}")
|
||||||
|
|
||||||
|
def _handle_parse_pdf(self):
|
||||||
|
"""Распарсить PDF через PyPDF2 (получает base64 тело от Lucee)."""
|
||||||
|
import base64
|
||||||
|
from PyPDF2 import PdfReader
|
||||||
|
import io as io_module
|
||||||
|
|
||||||
|
length = int(self.headers.get("Content-Length", 0))
|
||||||
|
body = self.rfile.read(length).decode("utf-8", errors="ignore").strip()
|
||||||
|
raw = base64.b64decode(body)
|
||||||
|
|
||||||
|
try:
|
||||||
|
reader = PdfReader(io_module.BytesIO(raw))
|
||||||
|
elements = []
|
||||||
|
for page in reader.pages:
|
||||||
|
text = page.extract_text()
|
||||||
|
if text:
|
||||||
|
for line in text.split('\n'):
|
||||||
|
line = line.strip()
|
||||||
|
if line:
|
||||||
|
elements.append({"type": "paragraph", "text": line, "style": ""})
|
||||||
|
result = {
|
||||||
|
"ok": True,
|
||||||
|
"status": "parsed",
|
||||||
|
"element_count": len(elements),
|
||||||
|
"elements": elements,
|
||||||
|
"tables": 0,
|
||||||
|
"paragraphs": len(elements),
|
||||||
|
}
|
||||||
|
except Exception as e:
|
||||||
|
result = {"ok": False, "status": "error", "error": str(e), "element_count": 0}
|
||||||
|
|
||||||
|
self.send_response(200)
|
||||||
|
self.send_header("Content-Type", "application/json; charset=utf-8")
|
||||||
|
self.send_header("Access-Control-Allow-Origin", "*")
|
||||||
|
self.end_headers()
|
||||||
|
self.wfile.write(json.dumps(result, ensure_ascii=False).encode())
|
||||||
|
|
||||||
def _handle_unzip_upload(self):
|
def _handle_unzip_upload(self):
|
||||||
"""Распаковать ZIP и загрузить каждый файл в Lucee. Только файлы из корня архива."""
|
"""Распаковать ZIP и загрузить каждый файл в Lucee. Только файлы из корня архива."""
|
||||||
import zipfile, io
|
import zipfile, io
|
||||||
|
|||||||
@@ -43,6 +43,11 @@ server {
|
|||||||
add_header Access-Control-Allow-Methods "GET, OPTIONS" always;
|
add_header Access-Control-Allow-Methods "GET, OPTIONS" always;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
location /parse-pdf {
|
||||||
|
proxy_pass http://127.0.0.1:8766;
|
||||||
|
proxy_read_timeout 60s;
|
||||||
|
}
|
||||||
|
|
||||||
location /upload {
|
location /upload {
|
||||||
if ($request_method = OPTIONS) {
|
if ($request_method = OPTIONS) {
|
||||||
add_header Access-Control-Allow-Origin "*";
|
add_header Access-Control-Allow-Origin "*";
|
||||||
|
|||||||
+12
-33
@@ -10,42 +10,21 @@
|
|||||||
<cfargument name="fileBytes" type="binary">
|
<cfargument name="fileBytes" type="binary">
|
||||||
<cfset var out = {elements: [], errors: []}>
|
<cfset var out = {elements: [], errors: []}>
|
||||||
<cftry>
|
<cftry>
|
||||||
<cfset var pdfDoc = 0>
|
<cfhttp url="https://contracts.kube5s.ru/parse-pdf" method="post" timeout="30">
|
||||||
<cftry>
|
<cfhttpparam type="body" value="#binaryEncode(fileBytes, 'base64')#">
|
||||||
<cfset pdfDoc = createObject("java", "org.apache.pdfbox.Loader").loadPDF(fileBytes)>
|
</cfhttp>
|
||||||
<cfcatch>
|
<cfif cfhttp.statusCode EQ 200>
|
||||||
<cftry>
|
<cfset var data = deserializeJSON(cfhttp.fileContent)>
|
||||||
<cfset pdfDoc = createObject("java", "org.apache.pdfbox.pdmodel.PDDocument").load(fileBytes)>
|
<cfif data.ok>
|
||||||
<cfcatch>
|
<cfset out.elements = data.elements>
|
||||||
<cfset arrayAppend(out.errors, "PDFBox: cannot open — " & cfcatch.message)>
|
<cfelse>
|
||||||
</cftry>
|
<cfset arrayAppend(out.errors, "VM parse error: " & (structKeyExists(data, 'error') ? data.error : 'unknown'))>
|
||||||
<cfif NOT isNumeric(pdfDoc)>
|
|
||||||
<cfset var stripper = createObject("java", "org.apache.pdfbox.text.PDFTextStripper")>
|
|
||||||
<cfset var pageCount = pdfDoc.getNumberOfPages()>
|
|
||||||
<cfloop from="1" to="#pageCount#" index="pn">
|
|
||||||
<cfset stripper.setStartPage(pn)>
|
|
||||||
<cfset stripper.setEndPage(pn)>
|
|
||||||
<cfset var pageText = stripper.getText(pdfDoc)>
|
|
||||||
<cfif len(trim(pageText))>
|
|
||||||
<cfloop list="#pageText#" index="line" delimiters="#chr(10)#">
|
|
||||||
<cfset line = trim(line)>
|
|
||||||
<cfif len(line)>
|
|
||||||
<cfset var el = structNew()>
|
|
||||||
<cfset el.type = "paragraph">
|
|
||||||
<cfset el.style = "">
|
|
||||||
<cfset el.text = line>
|
|
||||||
<cfset el.page = pn>
|
|
||||||
<cfset arrayAppend(out.elements, el)>
|
|
||||||
</cfif>
|
</cfif>
|
||||||
</cfloop>
|
<cfelse>
|
||||||
|
<cfset arrayAppend(out.errors, "VM HTTP error: " & cfhttp.statusCode)>
|
||||||
</cfif>
|
</cfif>
|
||||||
</cfloop>
|
|
||||||
<cfset pdfDoc.close()>
|
|
||||||
</cfif>
|
|
||||||
<cfset bis.close()>
|
|
||||||
<cfset bais.close()>
|
|
||||||
<cfcatch>
|
<cfcatch>
|
||||||
<cfset arrayAppend(out.errors, "PDFBox: " & cfcatch.message)>
|
<cfset arrayAppend(out.errors, "PDF parse failed: " & cfcatch.message)>
|
||||||
</cfcatch>
|
</cfcatch>
|
||||||
</cftry>
|
</cftry>
|
||||||
<cfreturn out>
|
<cfreturn out>
|
||||||
|
|||||||
Reference in New Issue
Block a user