2009-04-26 7 views
4

J'ai donc environ 4 000 documents Word que j'essaie d'extraire du texte et d'insérer dans une table db. Cela fonctionne nager jusqu'à ce que le processeur rencontre un document avec l'extension de fichier *.doc mais détermine que le fichier est en réalité un RTF. Maintenant, je sais que POI ne supporte pas les fichiers RTF, mais j'ai besoin d'un moyen de déterminer si un fichier *.doc est réellement un fichier RTF afin que je puisse choisir d'ignorer le fichier et continuer le traitement.Meilleur moyen de déterminer si le fichier * .doc est RTF avec Java ou ColdFusion

J'ai essayé plusieurs techniques pour surmonter cela, y compris en utilisant MimeTypeUtils de ColdFusion, cependant, il semble baser son hypothèse du type mime sur l'extension de fichier et classe toujours le RTF en application/msword. Existe-t-il un autre moyen de déterminer si un *.doc est un RTF? Toute aide serait grandement appréciée.

Répondre

5

Avec CF8 et compatible:

<cffunction name="IsRtfFile" returntype="Boolean" output="false"> 
    <cfargument name="FileName" type="String" /> 
    <cfreturn Left(FileRead(Arguments.FileName),5) EQ '{\rtf' /> 
</cffunction> 


Pour les versions antérieures:

<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> 


Mise à jour: Une meilleure CF8/réponse compatible. Pour éviter de charger le fichier en mémoire, vous pouvez effectuer les opérations suivantes pour charger seulement les premiers caractères:

<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> 


Sur la base des commentaires:
Voici un moyen très rapide comment vous pourrait faire un type de fonction "quel format est-ce". Pas parfait, mais ça vous donne l'idée ...

<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> 

Bien sûr, intéressant de souligner que tout cela ne fonctionnera pas sur les formats 'de', y compris sans en-tête beaucoup de ceux à base de texte commun (CFM, CSS, JS, etc.).

+0

C'est parfait! Par curiosité, cette technique pourrait-elle être utilisée pour détecter la version de Word avec laquelle le document a été créé? J'ai rencontré un autre problème où POI est en train de créer un ajustement sur un fichier qui, selon lui, a été créé avec Word 95. Sinon, pourrais-je simplement abandonner complètement les POI et charger les données tirées avec FileRead() et les charger dans la DB façon? En fin de compte, mon but est simplement d'avoir le texte du document disponible pour la recherche mais pas l'affichage. –

+0

Si vous pouvez identifier les séquences de marqueurs de fichier pour les différentes versions, cette technique pourrait être utilisée pour plusieurs formats, puisque beaucoup de formats de fichiers binaires commencent par jusqu'à 8 octets qui identifient le format de cette façon. –

+0

Pour lire les fichiers entiers ... bien, en utilisant FileRead traitera les fichiers en tant que texte - donc je ne sais pas si cela pourrait corrompre un document Word. Si c'est le cas, vous pouvez essayer FileReadBinary, mais je ne suis pas sûr alors si elle serait consultable en tant que texte dans votre base de données. –

7

Les cinq premiers octets dans un fichier RTF devrait être:

{\rtf 

Si elles ne sont pas, ce n'est pas un fichier RTF.

La section des liens externes du Wikipeida article est liée aux spécifications des différentes versions de RTF.

Les fichiers Doc (au moins ceux depuis Word '97) utilisent quelque chose appelé "Windows Compound Binary Format", documenté in a PDF here. D'après cela, ces fichiers Doc commencent par la séquence suivante:

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

Ou dans les fichiers bêta plus âgés:

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

Selon l'article de Wikipedia sur Word, il y avait au moins 5 différents formats avant 97

Vous cherchez {\ rtf devrait être votre meilleur pari.

Bonne chance, j'espère que cela aidera.

+0

Je ne remarqué dans une partie du code de POI qu'un PushbackInputStream est instancié qui tire un byteArray des 6 premiers octets. J'ai essayé la même chose du côté de coldfusion et j'ai réussi à obtenir le byteArray, mais je suis resté bloqué en essayant de comprendre comment convertir le byteArray en une chaîne lisible par CF pour que je puisse vérifier {\ rtf. Au lieu de cela, tout ce que je peux obtenir sont des nombres. Des idées? –

+0

Pouvez-vous juste faire une FileRead standard dessus? –

0

Vous pouvez essayer d'identifier les fichiers avec l'outil Droid (Digital Record Object Identification), qui donne accès au Pronom technical registry.

1

Vous pouvez convertir le byteArray à une chaîne

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

Vous pouvez également essayer les méthodes de hasxxxHeader de source de POI. Ils déterminent si un fichier d'entrée est un élément que POI peut gérer: OLE ou OOXML. Mais je crois que quelqu'un d'autre a suggéré d'utiliser un simple try/catch pour ignorer les fichiers de problèmes. Y a-t-il une raison pour laquelle vous ne souhaitez pas faire cela? Cela semblerait l'option la plus simple.

Mise à jour: suggestion de Pierre d'utiliser la fonction CF 8 travaillerait également

<cfset input = FileOpen(pathToYourFile)> 
<cfset bytes = FileRead(input , 8)> 
<cfdump var="#bytes#"> 
<cfset FileClose(input)> 
+0

Ah, encore mieux que la méthode de la boucle. Devrait probablement avoir un FileClose explicite (entrée) là aussi bien? –

+0

Oui, il devrait certainement avoir un FileClose explicite (..). J'ai oublié de copier cette ligne. –

Questions connexes