sábado, 22 de agosto de 2009

Como obtener el tipo de archivo usando C#

Hola a todos en esta ocasion vamos a ver como saber el tipo de extension de un archivo que por diversos motivos se puede perder; a raiz de un requerimiento en una empresa que por obvios motivos no mencionare su nombre se pidio elaborar un utilitario que permita recuperar el tipo de archivo que estaba guardado en sus librerias de documentos en Sharepoint.

Luego de investigar encontre que se podia obtener el tipo de archivo de 2 formas:
Obteniendo el MIME/TYPES (para mayor informacion ingrese aqui)
Leyendo su codigo hexadecimal (para mayor informacion ingrese aqui)
Antes de explicarles el codigo debemos tener en cuenta algunas consideraciones:
Al usar el MIME/TYPES no es 100% seguro porque solo guarda el ultimo tipo de archivo, por ejemplo si yo reemplazo la extension de un archivo .doc por una extension .xls el MIME/TYPES devolvera un tipo application/vnd.ms-excel ademas este proceso es muy lento porque para averiguar su tipo de archivo es necesario descargar el archivo en disco, sin embargo es una buena alternativa cuando se tiene que identificar archivos planos tipo .txt
Usando la lectura de su codigo hexadecimal es mucho mas eficiente porque leemos la firma hexadecimal de cualquier archivo, generalmente el tipo de archivo se encuentra grabado dentro de los 1024 bites que seria la cabecera, de ahi tenemos que ver la manera de saber que secciones de estos 1024 bites debemos leer.
Luego de evaluar se tuvo problemas al tratar de identificar los archivos tipo Excel, motivo por el cual se tubo que aumentar el ranog de lectura de archivos de 1024 a 2048.
Para el caso de los archivos Office 2007 se tiene que hacer un trabajo adicional pues como sabemos los archivos Office 2007 son archivos XML comprimidos; para saber el tipo de archivo se tiene que descomprimir y leer el archivo [Content_Types].xml y dentro de este archivo se tiene que leer los siguientes tag:
para DOCX
para XLSX
para pptx


Para descomprimir archivos les recomiendo usen librerias free, pueden encontrar esta aqui
Aca les dejo algunos fragmentos de codigo que espero les pueda servir (recordar que este utilitario fue creado para un requerimiento para MOSS pero lo pueden adaptar la logica de busqueda)

//Librerias utilizadas
using Microsoft.SharePoint;

using Microsoft.SharePoint.WebControls;

using Microsoft.SharePoint.WebPartPages;

using Microsoft.SharePoint.Utilities;

using System.IO;

using ICSharpCode.SharpZipLib.Zip;

using ICSharpCode.SharpZipLib.Zip.Compression.Streams;

using System.Xml;

Leemos cada uno de los archivos contenidos en una Libreria en Sharepoint
foreach (DataRow dr in dt.Rows)//Aca proceso cada carpeta

{

using (SPSite oSPSite = new SPSite(txtSite.Text)) {

using (SPWeb oSPWeb = oSPSite.OpenWeb()) {

string strLibrary = dr[0].ToString();

string strFolder = dr[1].ToString();

SPFolderCollection folders = oSPWeb.GetFolder(dr[0].ToString()).SubFolders;

SPFileCollection filesMyLibrary = folders[dr[1].ToString()].Files;

SPFile file = filesMyLibrary[dr[3].ToString()];

string strFileName = file.Name;

if (strFileName.Split('.').Length == 1)//Sin Extension { //Empiezo Evaluacion

Byte[] arrByte = file.OpenBinary();

byte[] bFile = new byte[2048];

Stream streamFile;

streamFile = file.OpenBinaryStream();

BinaryReader brFile = new BinaryReader(streamFile);

int count = brFile.Read(bFile, 0, bFile.Length - 1);

brFile.Close(); streamFile.Close();

//Preguntamos por que tipo de archivo es en formato Hexadecimal

string strExtension = String.Empty;

if (ExtensionEncontrada(ref strExtension, bFile, dr[2].ToString(), dr[3].ToString())) {

//Escribir en el log lo que se esta corrigiendo

file.MoveTo(strLibrary + "/" + strFolder + "/" + strFileName + strExtension, true); tw.WriteLine("\"{0}\",\"{1}\",\"{2}\",\"{3}\",\"{4}\",\"{5}\",\"{6}\"", "OK", DateTime.Now.ToString(), txtSite.Text, strLibrary, strFolder, strFileName + strExtension, strLogError); intArchivosCorregidos++;

}

else { //Escribir en el log que no se pudo hacer nada

tw.WriteLine("\"{0}\",\"{1}\",\"{2}\",\"{3}\",\"{4}\",\"{5}\",\"{6}\"", "NO OK", DateTime.Now.ToString(), txtSite.Text, strLibrary, strFolder, strFileName, strLogError); intArchivosSinCorregir++;

}

}

}

}

}

tw.Close();

tw.Dispose();

Ahora revisaremos el metodo booleano ExtensionEncontrada que recibe como parametros:
Una variable por referencia para devolver el tipo de extension
El arreglo de 2048 bites leidos del archivo
El nombre del archivo sin extension a evlauar
La ruta donde se encuentra el archivo
private bool ExtensionEncontrada(ref string strExtension, byte[] bFile, string strNombreArchivo, string strRutaArchivo) {

strLogError = String.Empty;

if (EsWav(bFile)) {

strExtension = ".wav";

return true;

}

else if (EsTiff(bFile)) {

strExtension = ".tiff";

return true;

}

else if (EsPDF(bFile)) {

strExtension = ".pdf";

return true;

}

else if (EsXML(bFile)) {

strExtension = ".xml";

return true;

}

else if (EsBMP(bFile)) {

strExtension = ".bmp";

return true;

}

else if (EsJPG(bFile)) {

strExtension = ".jpg";

return true;

}

else if (EsPNG(bFile)) {

strExtension = ".png";

return true;

}

else if (EsGIF(bFile)) {

strExtension = ".gif";

return true;

}

else if (EsGIF2(bFile)) {

strExtension = ".gif";

return true;

}

else if (EsDoc(bFile)) {

strExtension = ".doc";

return true;

}

else if (EsXls(bFile)) {

strExtension = ".xls";

return true;

}

else if (EsWma(bFile)) {

strExtension = ".wma";

return true;

}

else if (EsMP3(bFile)) {

strExtension = ".mp3";

return true;

}

else if (EsTXT(bFile, strNombreArchivo, strRutaArchivo)) {

strExtension = ".txt";

return true;

}

else if (EsPPT(bFile, ref strExtension, strNombreArchivo, strRutaArchivo)) {

strExtension = ".ppt";

return true;

}

else if (EsOffice2007(bFile, ref strExtension, strNombreArchivo, strRutaArchivo))//Necesita ser dezipiado por eso se evalue en otro lado

{

return true;

}

else

{

return false;

}

}

Este metodo esta a su vez compuesto por una serie de metodos que de acuerdo al tipo de archivo se leera ciertas posiciones en base al link de firmas de archivo en formato hexadecimal, para este ejemplo se revisara solo algunos metodos.
private bool EsWav(byte[] bFile) {

strLogError = String.Empty;

bool retVal = false;

try {

//52 49 46 46 xx xx xx xx 57 41 56 45 66 6D 74 20 RIFF....WAVEfmt

if (bFile[0] == 0x52 &&

bFile[1] == 0x49 &&

bFile[2] == 0x46 &&

bFile[3] == 0x46 &&

//bFile[4] == 0xxx &&

//bFile[5] == 0xxx &&

//bFile[6] == 0xxx &&

//bFile[7] == 0xxx &&

bFile[8] == 0x57 &&

bFile[9] == 0x41 &&

bFile[10] == 0x56 &&

bFile[11] == 0x45 &&

bFile[12] == 0x66 &&

bFile[13] == 0x6D &&

bFile[14] == 0x74 &&

bFile[15] == 0x20) {

retVal = true;

}

}

catch(Exception ex) {

strLogError = "Se produjo el siguiente error en EsWav: " + ex.Message;

retVal = false;

}

return (retVal);

}
private bool EsTiff(byte[] bFile) {

strLogError = string.Empty;

bool retVal = false;

try {

//49 49 2A 00 II*.

if (bFile[0] == 0x49 &&

bFile[1] == 0x49 &&

bFile[2] == 0x2A &&

bFile[3] == 0x00)

{

retVal = true;

}

}

catch (Exception ex) {

strLogError = "Se produjo el siguiente error en EsTiff: " + ex.Message;

retVal = false;

}

return (retVal);

}

private bool EsPDF(byte[] bFile) {

strLogError = string.Empty;

bool retVal = false;

try {

//25 50 44 46 %PDF

if (bFile[0] == 0x25 &&

bFile[1] == 0x50 &&

bFile[2] == 0x44 &&

bFile[3] == 0x46) {

retVal = true;

}

}

catch (Exception ex) {

strLogError = "Se produjo el siguiente error en EsPDF: " + ex.Message;

retVal = false;

}

return (retVal);

}

private bool EsXML(byte[] bFile) {

bool retVal = false;

//3C 3F 78 6D 6C 20 76 65 72 73 69 6F 6E 3D
private bool EsDoc(byte[] bFile) {

bool retVal = false;

//D0 CF 11 E0 A1 B1 1A E1

//[512 byte offset] [512 byte offset]

//EC A5 C1 00 .

if (bFile[0] == 0xD0 &&

bFile[1] == 0xCF &&

bFile[2] == 0x11 &&

bFile[3] == 0xE0 &&

bFile[4] == 0xA1 &&

bFile[5] == 0xB1 &&

bFile[6] == 0x1A &&

bFile[7] == 0xE1 &&

bFile[512] == 0xEC &&

bFile[513] == 0xA5 &&

bFile[514] == 0xC1 &&

bFile[515] == 0x00) {

retVal = true;

}

return (retVal);

}

private bool EsXls(byte[] bFile) {

bool retVal = false;

//D0 CF 11 E0 A1 B1 1A E1

//[512 byte offset] [512 byte offset]

//FD FF FF FF nn 02 ..

//where nn = 0x10, 0x22, 0x23, 0x28, or 0x29

//57 00 6F 00 72 00 6B 00 62 00 6F 00 6F 00 6B 00

if (bFile[0] == 0xD0 &&

bFile[1] == 0xCF &&

bFile[2] == 0x11 &&

bFile[3] == 0xE0 &&

bFile[4] == 0xA1 &&

bFile[5] == 0xB1 &&

bFile[6] == 0x1A &&

bFile[7] == 0xE1 &&

bFile[1152] == 0x57 &&

bFile[1153] == 0x00 &&

bFile[1154] == 0x6F &&

bFile[1155] == 0x00 &&

bFile[1156] == 0x72 &&

bFile[1157] == 0x00 &&

bFile[1158] == 0x6B &&

bFile[1159] == 0x00 &&

bFile[1160] == 0x62 &&

bFile[1161] == 0x00 &&

bFile[1162] == 0x6F &&

bFile[1163] == 0x00 &&

bFile[1164] == 0x6F &&

bFile[1165] == 0x00 &&

bFile[1166] == 0x6B &&

bFile[1167] == 0x00) {

retVal = true;

}

return (retVal);

}

private bool EsWma(byte[] bFile) {

bool retVal = false;

//30 26 B2 75 8E 66 CF 11 A6 D9 00 AA 00 62 CE 6C 0&u.f...bl

if (bFile[0] == 0x30 &&

bFile[1] == 0x26 &&

bFile[2] == 0xB2 &&

bFile[3] == 0x75 &&

bFile[4] == 0x8E &&

bFile[5] == 0x66 &&

bFile[6] == 0xCF &&

bFile[7] == 0x11 &&

bFile[8] == 0xA6 &&

bFile[9] == 0xD9 &&

bFile[10] == 0x00 &&

bFile[11] == 0xAA &&

bFile[12] == 0x00 &&

bFile[13] == 0x62 &&

bFile[14] == 0xCE &&

bFile[15] == 0x6C) {

retVal = true;

}

return (retVal);

}

private bool EsMP3(byte[] bFile) {

bool retVal = false;

//49 44 33 ID3

//FF Ex .

//FF Fx .

if (bFile[0] == 0x49 &&

bFile[1] == 0x44 &&

bFile[2] == 0x33) {

retVal = true;

}

return (retVal);

}
//Para el caso de TXT se usa el MIME TYPE
private bool EsTXT(byte[] bFile, string strNombreArchivo, string strRutaArchivo) {

strLogError = String.Empty;

bool retVal = false;

try {

//Averiguar Hexadecimal, sino usar el contentType

//Lo descargamos a una carpeta temporal

HttpWebRequest request;

HttpWebResponse response = null;

string strPath = txtSite.Text + strRutaArchivo;

request = (HttpWebRequest)WebRequest.Create(txtSite.Text + strRutaArchivo); request.Credentials = System.Net.CredentialCache.DefaultCredentials;

request.Timeout = 10000;

request.AllowWriteStreamBuffering = false;

response = (HttpWebResponse)request.GetResponse();

Stream s = response.GetResponseStream();

FileStream fs = new FileStream(strCarpetaTemporal + strNombreArchivo, FileMode.Create);

byte[] read = new byte[256];

int count = s.Read(read, 0, read.Length);

while (count > 0) {

fs.Write(read, 0, count);

count = s.Read(read, 0, read.Length);

}

//Cerrando lo que se haya leido

fs.Close();

s.Close();

response.Close();

//Averiguamos el ContentType del Archivo descargado

if (Util.getMimeFromFile(strCarpetaTemporal + strNombreArchivo) == "text/plain") {

retVal = true;

}

else {

retVal = false;

}

//Eliminamos el Archivo Analizado

File.Delete(strCarpetaTemporal + strNombreArchivo);

}

catch (Exception ex)

{

//throw new Exception("Error en metodo EsTXT: " + ex.Message);

strLogError = "Se produjo el siguiente error en EsTXT: " + ex.Message;

string strError = ex.Message;

retVal = false;

}

return (retVal);

}

//Para el caso de los archivos Office 2007 primero descomprimimos para averiguar el tipo de archivo
private bool EsOffice2007(byte[] bFile, ref string strExtension, string strNombreArchivo, string strRutaArchivo) {

// 50 4B 03 04 14 00 06 00 PK...... DOCX, PPTX, XLSX Office 2007 documents

strLogError = String.Empty;

bool retVal = false;

if (bFile[0] == 0x50 &&

bFile[1] == 0x4B &&

bFile[2] == 0x03 &&

bFile[3] == 0x04 &&

bFile[4] == 0x14 &&

bFile[5] == 0x00 &&

bFile[6] == 0x06 &&

bFile[7] == 0x00)

{

//Necesitamos dezipear para averiguar el tipo de Office 2007 que es

try { //Lo descargamos a una carpeta temporal

HttpWebRequest request;

HttpWebResponse response = null;

string strPath = txtSite.Text + strRutaArchivo;

request = (HttpWebRequest)WebRequest.Create(txtSite.Text + strRutaArchivo);

request.Credentials = System.Net.CredentialCache.DefaultCredentials;

request.Timeout = 10000; request.AllowWriteStreamBuffering = false;

response = (HttpWebResponse)request.GetResponse();

Stream st = response.GetResponseStream();

FileStream fs = new FileStream(strCarpetaTemporal + strNombreArchivo, FileMode.Create);

byte[] read = new byte[256];

int count = st.Read(read, 0, read.Length);

while (count > 0) {

fs.Write(read, 0, count);

count = st.Read(read, 0, read.Length);

}

//Cerrando lo que se haya leido

fs.Close();

st.Close();

response.Close();

//Le agregamos la extension .zip

File.Move(strCarpetaTemporal + strNombreArchivo, strCarpetaTemporal + strNombreArchivo + ".zip");

string directorioSalida = strCarpetaTemporal;

//Lo descomprimimos

using (ZipInputStream s = new ZipInputStream(File.OpenRead(strCarpetaTemporal + strNombreArchivo + ".zip"))) {

ZipEntry theEntry;

while ((theEntry = s.GetNextEntry()) != null) {

string theEntryDirectoryname = Path.GetDirectoryName(theEntry.Name);

if (theEntryDirectoryname.Length == 0) {

string directoryName = directorioSalida;

//Path.GetDirectoryName(theEntry.Name);

string fileName = Path.GetFileName(theEntry.Name);

// create directory

if (directoryName.Length > 0) {

Directory.CreateDirectory(directoryName);

}

if (fileName != String.Empty) {

using (FileStream streamWriter = File.Create(directoryName + theEntry.Name)) {

int size = 1024 * 2;

byte[] data = new byte[size];

while (true) {

size = s.Read(data, 0, data.Length);

if (size > 0) {

streamWriter.Write(data, 0, size);

}

else {

break;

}

}

}

}

}

}

}

string x = String.Empty;

//Leemos el content type para saber que tipo de extension es (Ojo: content type de archivo XML de documentos Office 2007)

XmlTextReader reader = new XmlTextReader(strCarpetaTemporal + "[Content_Types].xml");

string tipoDocumento = "0";

//1=docx, 2=xlsx, 3=pptx

while (reader.Read()) {

XmlNodeType nodeType = reader.NodeType;

switch (nodeType) {

case XmlNodeType.Element:

//Console.WriteLine("Element name is {0}", reader.Name);

if (reader.HasAttributes) {

for (int i = 0; i <>

reader.MoveToAttribute(i);

//Console.WriteLine("Attribute is {0} with Value {1}: ", reader.Name, reader.Value);

if (reader.Value == @"/word/document.xml") {

//Console.WriteLine("Es un documento Word 2007");

tipoDocumento = "1";

break;

}

else if (reader.Value == @"/xl/workbook.xml") {

//Console.WriteLine("Es un documento Excel 2007");

tipoDocumento = "2";

break;

}

else if (reader.Value == @"/ppt/presentation.xml") {

//Console.WriteLine("Es un documento Power Point 2007");

tipoDocumento = "3";

break;

}

else {

//Console.WriteLine("No es un documento office 2007");

}

}

//Console.WriteLine("No es un documento office 2007");

} break;

default:

//Console.WriteLine("No es un documento office 2007");

break;

//case XmlNodeType.Text:

// Console.WriteLine("Value is: " + reader.Value);

// break;

}

}

reader.Close();

//Asignamos la extension correcta y devolvemos true

if (tipoDocumento == "1") {

strExtension = ".docx";

retVal = true;

//Console.WriteLine("Es un documento word 2007");

}

if (tipoDocumento == "2") {

strExtension = ".xlsx";

retVal = true;

//Console.WriteLine("Es un documento excel 2007");

}

if (tipoDocumento == "3") {

strExtension = ".pptx";

retVal = true;

//Console.WriteLine("Es un documento power point 2007");

}

if (tipoDocumento == "0") {

strExtension = String.Empty;

retVal = false;

//Console.WriteLine("No es un documento office 2007");

}

//Obtenemos los directorios para empezar a eliminarlos

//DirectoryInfo[] di = new DirectoryInfo(strCarpetaTemporal).GetDirectories();

//Eliminamos la carpeta descomprimida

//di[0].Delete(true);

//Eliminamos el archivo bajado en la carpeta temporal y el archivo xml descomprimido

File.Delete(strCarpetaTemporal + strNombreArchivo + ".zip");

File.Delete(strCarpetaTemporal + "[Content_Types].xml");

}

catch (Exception ex) {

strLogError = "Se produjo el siguiente Error en EsOffice2007: " + ex.Message;

//throw new Exception("Error en EsOffice2007: " + ex.Message);

string strError = ex.Message;

retVal = false;

}

}

return (retVal);

}

//Aca les dejo el metodo MIME TYPE para obtener el tipo de archivo
Librerias Utilizadas:
using System.Diagnostics;

using System.Windows.Forms;

using System.Runtime.InteropServices;

using System.Security.Permissions;


//Otro Metodo para obtener el tipo de Archivo

[System.Runtime.InteropServices.DllImport("urlmon.dll", EntryPoint = "FindMimeFromData", ExactSpelling = true, CharSet = System.Runtime.InteropServices.CharSet.Ansi, SetLastError = true)] public static extern int FindMimeFromData(IntPtr pBC, [MarshalAs(UnmanagedType.LPWStr)] string pwzUrl, [MarshalAs(UnmanagedType.LPArray)] byte[] pBuffer, int cbSize, [MarshalAs(UnmanagedType.LPWStr)] string pwzMimeProposed, int dwMimeFlags, [MarshalAs(UnmanagedType.LPWStr)] ref string ppwzMimeOut, int dwReserved);

public static string getFileTypeFromFile(string file) {

string[] array1 = file.Split('\\');

string fileName = String.Empty;

fileName = array1[array1.Length - 1].ToString();

//Validar que no tenga extension para que entre al metodo

if (fileName.Split('.').Length == 1)

//Es un archivo sin extension, entro a evaluar

{

string mimeType = getMimeFromFile(file);

switch (mimeType) {

case "audio/wav":

fileName = fileName + ".wav";

break;

case "application/pdf":

fileName = fileName + ".pdf";

break;

case "text/plain":

fileName = fileName + ".txt";

break;

case "text/xml":

fileName = fileName + ".xml";

break;

case "image/bmp":

fileName = fileName + ".bmp";

break;

case "image/pjpeg":

fileName = fileName + ".jpg";

break;

case "image/x-png":

fileName = fileName + ".png";

break;

case "image/gif":

fileName = fileName + ".gif";

break;

case "application/octet-stream":

//.doc, .xls, .ppt, .wma, .mp3, .tif, .tiff

fileName = mimeType;

break;

case "application/x-zip-compressed":

//.docx, pptx, xlsx fileName = mimeType;

break;

default:

//fileName = "sin formato conocido";

fileName = mimeType;

break;

}

}

return fileName;

}

public static string getMimeFromFile(string file) {

string mimeout = "";

int MaxContent = 0;

FileStream fs = null;

byte[] buf = null;

int result = 0;

if (!(System.IO.File.Exists(file))) {

throw new FileNotFoundException(file + " not found");

}

if (MaxContent > 4096) {

MaxContent = 4096;

}

MaxContent = System.Convert.ToInt32(new FileInfo(file).Length);

fs = new FileStream(file, FileMode.Open);

buf = new byte[MaxContent + 1];

fs.Read(buf, 0, MaxContent);

fs.Close();

result = FindMimeFromData(IntPtr.Zero, file, buf, MaxContent, null, 0, ref mimeout, 0);

return mimeout;

}

Aca algunos imagenes de la funcionalidad del aplicativo

En esta lista encontramos algunos archivos sin extension


Observamos el detalle de un archivo BMP sin extension


Luego de usar estos metodos a traves de un utilitario vemos que los archivos son corregidos



Volvemos a ver el mismo archivo pero esta vez ya con su adecuada extension







































No hay comentarios:

Publicar un comentario