Лучший способ определить, является ли файл * .doc RTF с помощью Java или ColdFusion
-
16-09-2019 - |
Вопрос
Итак, у меня есть около 4000 документов Word, из которых я пытаюсь извлечь текст и вставить в таблицу базы данных.Это работает плавно до тех пор, пока процессор не встретит документ с *.doc
расширение файла, но определяет, что файл на самом деле является RTF.Теперь я знаю, что POI не поддерживает RTFS, что нормально, но мне действительно нужен способ определить, является ли *.doc
файл на самом деле является RTF, так что я могу проигнорировать файл и продолжить обработку.
Я попробовал несколько методов, чтобы преодолеть это, включая использование MimeTypeUtils от ColdFusion, однако, похоже, что он основывает свое предположение о mimetype на расширении файла и по-прежнему классифицирует RTF как application / msword.Есть ли какой-либо другой способ определить, является ли *.doc
является ли RTF?Любая помощь была бы чрезвычайно признательна.
Решение
С CF8 и совместимый:
<cffunction name="IsRtfFile" returntype="Boolean" output="false">
<cfargument name="FileName" type="String" />
<cfreturn Left(FileRead(Arguments.FileName),5) EQ '{\rtf' />
</cffunction>
Для более ранних версий:
<cffunction name="IsRtfFile" returntype="Boolean" output="false">
<cfargument name="FileName" type="String" />
<cfset var FileData = 0 />
<cffile variable="FileData" action="read" file="#Arguments.FileName#" />
<cfreturn Left(FileData,5) EQ '{\rtf' />
</cffunction>
Обновить: Лучший ответ, совместимый с CF8 /.Чтобы избежать загрузки всего файла в память, вы можете сделать следующее, чтобы загрузить только первые несколько символов:
<cffunction name="IsRtfFile" returntype="Boolean" output="false">
<cfargument name="FileName" type="String" />
<cfset var FileData = 0 />
<cfloop index="FileData" file="#Arguments.FileName#" characters="5">
<cfbreak/>
</cfloop>
<cfreturn FileData EQ '{\rtf' />
</cffunction>
Основываясь на комментариях:
Вот очень быстрый способ, как вы могли бы сгенерировать функцию типа "какой это формат".Не идеально, но это дает вам представление...
<cffunction name="determineFileFormat" returntype="String" output="false"
hint="Determines format of file based on header of the file's data."
>
<cfargument name="FileName" type="String"/>
<cfset var FileData = 0 />
<cfset var CurFormat = 0 />
<cfset var MaxBytes = 8 />
<cfset var Formats =
{ WordNew : 'D0,CF,11,E0,A1,B1,1A,E1'
, WordBeta : '0E,11,FC,0D,D0,CF,11,E0'
, Rtf : '7B,5C,72,74,66' <!--- {\rtf --->
, Jpeg : 'FF,D8'
}/>
<cfloop index="FileData" file="#Arguments.FileName#" characters="#MaxBytes#">
<cfbreak/>
</cfloop>
<cfloop item="CurFormat" collection="#Formats#">
<cfif Left( FileData , ListLen(Formats[CurFormat]) ) EQ convertToText(Formats[CurFormat]) >
<cfreturn CurFormat />
</cfif>
</cfloop>
<cfreturn "Unknown"/>
</cffunction>
<cffunction name="convertToText" returntype="String" output="false">
<cfargument name="HexList" type="String" />
<cfset var Result = "" />
<cfset var CurItem = 0 />
<cfloop index="CurItem" list="#Arguments.HexList#">
<cfset Result &= Chr(InputBaseN(CurItem,16)) />
</cfloop>
<cfreturn Result />
</cffunction>
Конечно, стоит отметить, что все это не будет работать в форматах без заголовков, включая многие распространенные текстовые форматы (CFM, CSS, JS и т.д.).
Другие советы
Первые пять байтов в любом RTF - файле должны быть:
{\rtf
Если это не так, то это не RTF-файл.
Раздел "внешние ссылки" в Статья в Википедии ссылка на спецификации для различных версий RTF.
Файлы Doc (по крайней мере, начиная с Word '97) используют нечто, называемое "Составной двоичный формат Windows", документированный в формате PDF здесь.В соответствии с этим, эти файлы Doc начинаются со следующей последовательности:
0xd0, 0xcf, 0x11, 0xe0, 0xa1, 0xb1, 0x1a, 0xe1
Или в старых бета-файлах:
0x0e, 0x11, 0xfc, 0x0d, 0xd0, 0xcf, 0x11, 0xe0
Согласно статье Википедии о Word, до 97 года существовало по крайней мере 5 различных форматов.
Поиск {\ rtf должен быть вашим лучшим выбором.
Удачи, надеюсь, это поможет.
Вы можете преобразовать ByteArray в строку
<cfset str = createObject("java", "java.lang.String").init(bytes)>
Вы также можете попробовать методы hasxxxHeader из исходного кода POI.Они определяют, является ли входной файл тем, что POI может обработать:OLE или OOXML.Но я полагаю, что кто-то другой предложил использовать простую попытку / catch, чтобы пропустить проблемные файлы.Есть ли причина, по которой вы не хотите этого делать?Казалось бы, это самый простой вариант.
Обновить: Предложение Питера использовать функцию CF 8 также сработало бы
<cfset input = FileOpen(pathToYourFile)>
<cfset bytes = FileRead(input , 8)>
<cfdump var="#bytes#">
<cfset FileClose(input)>
Вы могли бы попробовать идентифицировать файлы с помощью Дроид инструмент (идентификация объекта цифровой записи), который обеспечивает доступ к Технический реестр Pronom.