249 lines
13 KiB
Plaintext
249 lines
13 KiB
Plaintext
<cfsetting showdebugoutput="no" enablecfoutputonly="true">
|
||
<cfheader name="Content-Type" value="application/json; charset=utf-8">
|
||
|
||
<cfparam name="url.doc_id" default="">
|
||
|
||
<cfset result = {ok: false, error: ""}>
|
||
|
||
<cftry>
|
||
<cfif NOT len(url.doc_id)>
|
||
<cfset result.error = "doc_id required">
|
||
<cfelse>
|
||
<!--- Читаем документ из БД --->
|
||
<cfquery name="doc" datasource="baza">
|
||
SELECT id, filename, mime_type, original_bytes, status
|
||
FROM documents
|
||
WHERE id = <cfqueryparam value="#url.doc_id#" cfsqltype="cf_sql_varchar">
|
||
</cfquery>
|
||
|
||
<cfif doc.recordCount EQ 0>
|
||
<cfset result.error = "document not found">
|
||
<cfelse>
|
||
<cfset elements = []>
|
||
<cfset errors = []>
|
||
|
||
<!--- PDF --->
|
||
<cfif doc.mime_type EQ "application/pdf">
|
||
<cftry>
|
||
<!--- Base64 → байты --->
|
||
<cfset fileBytes = binaryDecode(doc.original_bytes, "base64")>
|
||
<!--- Байты → InputStream --->
|
||
<cfset bais = createObject("java", "java.io.ByteArrayInputStream").init(fileBytes)>
|
||
<cfset bis = createObject("java", "java.io.BufferedInputStream").init(bais)>
|
||
|
||
<!--- Пробуем PDFBox 3.x Loader, иначе 2.x PDDocument.load --->
|
||
<cfset pdfDoc = 0>
|
||
<cftry>
|
||
<cfset pdfDoc = createObject("java", "org.apache.pdfbox.Loader").loadPDF(bis)>
|
||
<cfcatch>
|
||
<cftry>
|
||
<cfset pdfDoc = createObject("java", "org.apache.pdfbox.pdmodel.PDDocument").load(bis)>
|
||
<cfcatch>
|
||
<cfset arrayAppend(errors, "PDFBox: cannot open PDF — " & cfcatch.message)>
|
||
</cfcatch>
|
||
</cftry>
|
||
</cfcatch>
|
||
</cftry>
|
||
<cfif NOT isNumeric(pdfDoc)>
|
||
<cfset stripper = createObject("java", "org.apache.pdfbox.text.PDFTextStripper")>
|
||
|
||
<cfset pageCount = pdfDoc.getNumberOfPages()>
|
||
<cfloop from="1" to="#pageCount#" index="pn">
|
||
<cfset stripper.setStartPage(pn)>
|
||
<cfset stripper.setEndPage(pn)>
|
||
<cfset pageText = stripper.getText(pdfDoc)>
|
||
<cfif len(trim(pageText))>
|
||
<cfloop list="#pageText#" index="line" delimiters="#chr(10)#">
|
||
<cfset line = trim(line)>
|
||
<cfif len(line)>
|
||
<cfset arrayAppend(elements, {type: "paragraph", style: "", text: line, page: pn})>
|
||
</cfif>
|
||
</cfloop>
|
||
</cfif>
|
||
</cfloop>
|
||
|
||
<cfset pdfDoc.close()>
|
||
</cfif>
|
||
<cfset bis.close()>
|
||
<cfset bais.close()>
|
||
|
||
<cfcatch>
|
||
<cfset arrayAppend(errors, "PDFBox: " & cfcatch.message)>
|
||
</cfcatch>
|
||
</cftry>
|
||
|
||
<!--- DOCX — ZIP/XML (POI не нужен) --->
|
||
<cfelseif doc.mime_type EQ "application/vnd.openxmlformats-officedocument.wordprocessingml.document">
|
||
<cftry>
|
||
<cfset fileBytes = binaryDecode(doc.original_bytes, "base64")>
|
||
<cfset bais = createObject("java", "java.io.ByteArrayInputStream").init(fileBytes)>
|
||
<cfset zis = createObject("java", "java.util.zip.ZipInputStream").init(bais)>
|
||
|
||
<cfset xmlStr = "">
|
||
<cfloop condition="true">
|
||
<cfset entry = zis.getNextEntry()>
|
||
<cfif isNull(entry)><cfbreak></cfif>
|
||
<cfif entry.getName() EQ "word/document.xml">
|
||
<cftry>
|
||
<cfset xmlBytes = zis.readAllBytes()>
|
||
<cfset xmlStr = createObject("java", "java.lang.String").init(xmlBytes, "UTF-8")>
|
||
<cfcatch>
|
||
<!--- fallback: читаем через буфер --->
|
||
<cfset baos = createObject("java", "java.io.ByteArrayOutputStream").init()>
|
||
<cfloop condition="true">
|
||
<cfset b = zis.read()>
|
||
<cfif b LT 0><cfbreak></cfif>
|
||
<cfset baos.write(b)>
|
||
</cfloop>
|
||
<cfset xmlStr = createObject("java", "java.lang.String").init(baos.toByteArray(), "UTF-8")>
|
||
</cfcatch>
|
||
</cftry>
|
||
<cfbreak>
|
||
</cfif>
|
||
<cfset zis.closeEntry()>
|
||
</cfloop>
|
||
<cfset zis.close()>
|
||
<cfset bais.close()>
|
||
|
||
<cfif NOT len(xmlStr)>
|
||
<cfset arrayAppend(errors, "DOCX: word/document.xml not found in ZIP")>
|
||
<cfelse>
|
||
<cfset docXml = xmlParse(xmlStr)>
|
||
<cfset ns = "http://schemas.openxmlformats.org/wordprocessingml/2006/main">
|
||
|
||
<!--- Ищем body: или с namespace или без --->
|
||
<cfset body = "">
|
||
<cftry>
|
||
<cfset body = xmlSearch(docXml, "//:body")>
|
||
<cfcatch>
|
||
<cfset body = xmlSearch(docXml, "//*[local-name()='body']")>
|
||
</cfcatch>
|
||
</cftry>
|
||
|
||
<cfif arrayLen(body) GT 0>
|
||
<cfset body = body[1]>
|
||
<cfloop array="#body.XmlChildren#" index="child">
|
||
<cfset localName = child.XmlName>
|
||
<cfif localName EQ "p" OR right(localName, 2) EQ ":p">
|
||
<!--- Параграф: собираем текст из всех w:t --->
|
||
<cfset paraText = "">
|
||
<cftry>
|
||
<cfset textNodes = xmlSearch(child, "//:t")>
|
||
<cfcatch>
|
||
<cfset textNodes = xmlSearch(child, "//*[local-name()='t']")>
|
||
</cfcatch>
|
||
</cftry>
|
||
<cfloop array="#textNodes#" index="tn">
|
||
<cfset paraText &= tn.XmlText>
|
||
</cfloop>
|
||
<cfset paraText = trim(paraText)>
|
||
<cfif len(paraText)>
|
||
<cfset arrayAppend(elements, {type: "paragraph", style: "", text: paraText, page: 0})>
|
||
</cfif>
|
||
<cfelseif localName EQ "tbl" OR right(localName, 4) EQ ":tbl">
|
||
<!--- Таблица --->
|
||
<cfset rows = []>
|
||
<cftry>
|
||
<cfset trNodes = xmlSearch(child, "//:tr")>
|
||
<cfcatch>
|
||
<cfset trNodes = xmlSearch(child, "//*[local-name()='tr']")>
|
||
</cfcatch>
|
||
</cftry>
|
||
<cfloop array="#trNodes#" index="trNode">
|
||
<cfset row = []>
|
||
<cftry>
|
||
<cfset tcNodes = xmlSearch(trNode, ".//:tc")>
|
||
<cfcatch>
|
||
<cfset tcNodes = xmlSearch(trNode, ".//*[local-name()='tc']")>
|
||
</cfcatch>
|
||
</cftry>
|
||
<cfloop array="#tcNodes#" index="tcNode">
|
||
<cfset cellText = "">
|
||
<cftry>
|
||
<cfset tNodes = xmlSearch(tcNode, ".//:t")>
|
||
<cfcatch>
|
||
<cfset tNodes = xmlSearch(tcNode, ".//*[local-name()='t']")>
|
||
</cfcatch>
|
||
</cftry>
|
||
<cfloop array="#tNodes#" index="tNode">
|
||
<cfset cellText &= tNode.XmlText>
|
||
</cfloop>
|
||
<cfset arrayAppend(row, trim(cellText))>
|
||
</cfloop>
|
||
<cfif arrayLen(row) GT 0>
|
||
<cfset arrayAppend(rows, row)>
|
||
</cfif>
|
||
</cfloop>
|
||
<cfif arrayLen(rows) GT 0>
|
||
<cfset arrayAppend(elements, {type: "table", rows: rows, page: 0})>
|
||
</cfif>
|
||
</cfif>
|
||
</cfloop>
|
||
<cfelse>
|
||
<cfset arrayAppend(errors, "DOCX: body not found in document.xml")>
|
||
</cfif>
|
||
</cfif>
|
||
|
||
<cfcatch>
|
||
<cfset arrayAppend(errors, "DOCX: " & cfcatch.message)>
|
||
</cfcatch>
|
||
</cftry>
|
||
|
||
<!--- .doc — старый формат, пропускаем --->
|
||
<cfelseif doc.mime_type EQ "application/msword">
|
||
<cfset arrayAppend(errors, ".doc parsing not available (use libreoffice to convert)")>
|
||
|
||
<!--- TXT — plain text --->
|
||
<cfelseif doc.mime_type EQ "text/plain" OR doc.mime_type EQ "application/octet-stream">
|
||
<cftry>
|
||
<cfset fileBytes = binaryDecode(doc.original_bytes, "base64")>
|
||
<cfset text = createObject("java", "java.lang.String").init(fileBytes, "UTF-8")>
|
||
<cfloop list="#text#" index="line" delimiters="#chr(10)#">
|
||
<cfset line = trim(line)>
|
||
<cfif len(line)>
|
||
<cfset arrayAppend(elements, {type: "paragraph", style: "", text: line, page: 1})>
|
||
</cfif>
|
||
</cfloop>
|
||
<cfcatch>
|
||
<cfset arrayAppend(errors, "TXT: " & cfcatch.message)>
|
||
</cfcatch>
|
||
</cftry>
|
||
|
||
<cfelse>
|
||
<cfset arrayAppend(errors, "unsupported mime_type: " & doc.mime_type)>
|
||
</cfif>
|
||
|
||
<!--- Сохраняем результат --->
|
||
<cfset newStatus = arrayLen(elements) GT 0 ? "parsed" : "error">
|
||
<cfif arrayLen(errors) GT 0 AND arrayLen(elements) EQ 0>
|
||
<cfset newStatus = "error">
|
||
</cfif>
|
||
|
||
<cfquery datasource="baza">
|
||
UPDATE documents
|
||
SET elements_json = <cfqueryparam value="#serializeJSON(elements)#" cfsqltype="cf_sql_varchar">::jsonb,
|
||
status = <cfqueryparam value="#newStatus#" cfsqltype="cf_sql_varchar">,
|
||
error_message = <cfqueryparam value="#arrayLen(errors) ? serializeJSON(errors) : ''#" cfsqltype="cf_sql_varchar">
|
||
WHERE id = <cfqueryparam value="#url.doc_id#" cfsqltype="cf_sql_varchar">
|
||
</cfquery>
|
||
|
||
<cfset result = {
|
||
ok: true,
|
||
doc_id: url.doc_id,
|
||
filename: doc.filename,
|
||
mime_type: doc.mime_type,
|
||
status: newStatus,
|
||
element_count: arrayLen(elements),
|
||
page_count: isDefined("pageCount") ? pageCount : 0,
|
||
errors: errors
|
||
}>
|
||
</cfif>
|
||
</cfif>
|
||
|
||
<cfcatch>
|
||
<cfset result = {ok: false, error: cfcatch.message, detail: cfcatch.detail}>
|
||
</cfcatch>
|
||
</cftry>
|
||
|
||
<cfoutput>#serializeJSON(result)#</cfoutput>
|