Лучший способ определить, является ли файл * .doc RTF с Java или ColdFusion - PullRequest
4 голосов
/ 26 апреля 2009

Итак, у меня есть около 4000 словарных документов, из которых я пытаюсь извлечь текст и вставить в таблицу БД. Это работает плавно, пока процессор не обнаружит документ с расширением *.doc, но определит, что файл на самом деле является RTF. Теперь я знаю, что POI не поддерживает RTF, что нормально, но мне нужен способ определить, действительно ли файл *.doc является RTF, чтобы я мог проигнорировать файл и продолжить обработку.

Я попробовал несколько методов, чтобы преодолеть это, включая использование MimeTypeUtils от ColdFusion, однако, похоже, он основывает свое предположение о mimetype на расширении файла и все же классифицирует RTF как application / msword. Есть ли другой способ определить, является ли *.doc RTF? Любая помощь будет принята с благодарностью.

Ответы [ 4 ]

7 голосов
/ 26 апреля 2009

Первые пять байтов в любом файле RTF должны быть:

{\rtf

Если это не так, это не файл RTF.

Раздел внешних ссылок в статье Wikipeida ссылается на спецификации для различных версий RTF.

В документах Doc (по крайней мере, начиная с Word '97) используется что-то, называемое «двоичный формат Windows Compound», задокументированный в PDF здесь . В соответствии с этим, эти Doc-файлы начинаются со следующей последовательности:

0xd0, 0xcf, 0x11, 0xe0, 0xa1, 0xb1, 0x1a, 0xe1

Или в более старых бета-файлах:

0x0e, 0x11, 0xfc, 0x0d, 0xd0, 0xcf, 0x11, 0xe0

Согласно статье в Википедии о Word, до 97 года было как минимум 5 различных форматов.

Поиск {\ rtf должен быть вашим лучшим выбором.

Удачи, надеюсь, это поможет.

5 голосов
/ 26 апреля 2009

С 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 и т. Д.).

1 голос
/ 27 апреля 2009

Вы можете преобразовать byteArray в строку

<cfset str = createObject("java", "java.lang.String").init(bytes)>

Вы также можете попробовать методы hasxxxHeader из источника POI. Они определяют, может ли входной файл обрабатывать POI: OLE или OOXML. Но я полагаю, что кто-то другой предложил использовать простую команду try / catch, чтобы пропустить файлы с проблемами Есть ли причина, по которой вы не хотите этого делать? Казалось бы, более простой вариант.

Обновление: Предложение Питера об использовании функции CF 8 также сработает

<cfset input = FileOpen(pathToYourFile)>
<cfset bytes = FileRead(input , 8)>
<cfdump var="#bytes#">
<cfset FileClose(input)>
0 голосов
/ 26 апреля 2009

Вы можете попробовать идентифицировать файлы с помощью инструмента Droid (цифровая идентификация объектов записи), который обеспечивает доступ к техническому реестру Pronom .

...