Sunteți pe pagina 1din 16

DESTRIPANDO ARCHIVOS ADJUNTOS1

A partir de la versión Access 2007 tenemos la posibilidad de


adjuntar archivos directamente en nuestras tablas. ¿Y cómo
funciona esto de los archivos adjuntos? Pues antes de que
este humilde servidor suelte una parrafada y se equivoque
os remito a la gran explicación (je, je...) que proporciona
Microsoft sobre lo que son y cómo trabajar con archivos
adjuntos como “usuario normal”, que podréis encontrar en
este (enlace) o, directamente, este (pdf).

Sin embargo, el objetivo de este ejemplo es intentar explicaros cómo podemos manejar los
ficheros adjuntos a través de código VBA. No es complicado (?), pero su manipulación requiere
tener en cuenta ciertas características “especiales” que vamos a ir conociendo a lo largo del
ejemplo (o, al menos, voy a intentar presentároslas de la manera más sencilla de que sea
capaz). ;-)

Para hacerlo lo más operativo posible, es decir, de cara a que podáis aplicarlo a cualquier BD,
el grueso del trabajo lo vamos a hacer a través de módulos. De ahí que sea importante, si no
estáis muy duchos en el tema, que analicéis bien este pdf que tenéis ahora entre las manos
para entender cómo se hacen las llamadas a los diferentes procedimientos de los módulos,
antes de lanzaros a intentar hacer directamente un copy-paste de la BD de ejemplo. Además
de eso, el formulario donde vayáis a trabajar ha de tener unos “requerimientos mínimos” que
debéis aplicar para que el invento nos funcione correctamente.

Y, abundando en el tema, la mecánica del ejemplo se va a basar en un identificador


autonumérico. Aunque podría basarse en otro tipo de identificador de registros, la comodidad
de uso de ese identificador me permite optimizar temas de código. Así que también os animo a
que lo uséis.

Evidentemente todo lo anterior es sólo una recomendación… ;-)

Manos a la obra, pero antes de que oscurezca…

UNOS OSCUROS COMENTARIOS TÉCNICOS J


Con sus más y sus menos, la aparición de la figura de los datos adjuntos viene a “sustituir” lo
que conocemos como los campos OLE. ¡Atención! No es que los sustituya en absoluto, sino que
utilizo la expresión anterior para intentar dar una idea clara de lo que significa la aparición de
los adjuntos. Ya sabemos que a un campo OLE podemos asociarle un archivo; en los datos
adjuntos podemos asociar más de un archivo, y además de diferentes tipos.

También son interesantes frente a los OLE porque son más eficientes, en el sentido que
mejoran el rendimiento porque comprimen los archivos que almacenan (obviamente versus
OLE).

¿Tienen algo negativo? Pues que no son manipulables a través de una SQL de actualización de
datos. Tampoco pueden ser utilizados en consultas de unión. Finalmente, existen ciertas
limitaciones para trabajar con ellos a través de macros de datos L

Para finalizar, y esto lo pongo porque lo he leído (no recuerdo dónde, así que no puedo poneros
la fuente) pero, al no utilizarlo, pues no hablo con un 100% de seguridad por no haberlo
podido probar, los datos adjuntos no son compatibles con los tipos de datos de SQL Server, y

1 La BD de ejemplo os la podéis bajar aquí

1
Visítame en http://neckkito.siliconproject.com.ar
necesitan un proceso de conversión a OLE antes de poder migrar los datos.

Y ahora sí. Tras estas “curiosidades técnicas”, y con la


llegada del ocaso, vamos a por faena.

NUESTRA TABLA...
Vamos a partir de una tabla muy simple, que llamaremos
TExpedientes. Su estructura será la siguiente:

… Y NUESTRO FORMULARIO
Sobre la tabla anterior vamos a crearnos un formulario que, curiosamente, llamaremos
FExpedientes.

Lo primero que vamos a hacer en ese formulario es eliminar el campo de los ficheros adjuntos.
Si no, ¿qué gracia tendría el ejemplo? 

PRIMER PASO: VER QUÉ ARCHIVOS ADJUNTOS HAY GUARDADOS


Antes de centrarnos propiamente en lo que es ver los archivos adjuntos en el formulario voy a
enseñaros cómo pueden manipularse los adjuntos a través del objeto-consulta. Podríamos
hacerlo a través de código VBA, pero dado que simplemente queremos “ver” una lista de
adjuntos así aprenderemos sus tratamientos posibles a través de una consulta de selección.

El funcionamiento es muy similar al que os explicaba en el ejemplo “Campos Multivalor”, que


podéis encontrar en mi web. Pero bueno, lo explicaremos aquí para que no quepa sombra de
duda alguna.

Vamos a crearnos una consulta, que llamaremos CAdjuntosSimple, que se basará en la tabla
TExpedientes. Al ir construyéndola, ¿nos llama algo la atención?

Pues la respuesta debería ser un rotundo “sí”, dado que la tabla que nos muestra la consulta
nos muestra, además, tres subcampos del campo que contiene los adjuntos.

2
Visítame en http://neckkito.siliconproject.com.ar
Es decir, tenemos “FileData”, “FileName” y “FileType”.

La primera forma de crear la consulta sería arrastrando


directamente, entre otros, el campo “raíz” de los adjuntos,
en nuestro caso [ArchAdj], con lo que la consulta nos
quedaría así:

Y que nos mostraría unos datos así:

Dicho en otro idioma, si nos construimos la consulta de esta manera estaríamos emulando lo
que veríamos en la tabla en vista Hoja de Datos.

Vamos a por la segunda manera de construir la consulta. Vamos a crearnos una consulta que
llamaremos CAdjuntos, con la siguiente estructura:

3
Visítame en http://neckkito.siliconproject.com.ar
Y que al situarla en vista Hoja de Datos nos devuelve la siguiente información:

Es decir, nos dice que:

“FileData” es el propio adjunto en sí (una nebulosa de


bytes… je, je...).
“FileName” es el nombre del archivo, es decir, un Word
llamado Adj1 y un Excel llamado Adj1
“FileType” nos dice que el Word es de tipo “docx” y que el Excel es de tipo “xlsx”.

Gracias a lo anterior ya sabemos que el adjunto tiene tres subcampos, ya sabemos cómo se
llaman y ya sabemos qué tipo de información muestran. Y eso es importante recordarlo en el
momento en que vayamos a utilizar VBA.

Ahora sí, volvamos a nuestro formulario. Para poder ver los adjuntos vamos a insertar un
cuadro de lista, que llamaremos lstAdjuntos2, y a través del asistente estableceremos la
siguiente configuración:

 Deseo buscar los valores en una consulta


 Seleccionamos la consulta CAdjuntos
 Seleccionamos el campo del FileName

 Ordenamos a nuestro gusto, si queremos.


 Redimensionamos a nuestro gusto, si queremos.
 Recordamos el valor para utilizarlo más adelante
 Le ponemos el nombre a la etiqueta del control.

¡Recordad que el cuadro de lista tiene que llamarse lstAdjuntos!

Ahora el cuadro de lista nos sacará todos los adjuntos que haya, que es lo que nos saca la
consulta de origen, pero sin filtrar por el registro en el que estamos. Para solucionar este
“inconveniente” sacamos las propiedades del cuadro de lista y nos vamos a Pestaña Datos >
Origen de la Fila, y sacamos la consulta subyacente haciendo clic sobre el pequeño botón de
puntos suspensivos.

Una vez tengamos esa consulta subyacente a la vista le añadimos un filtro a través del
identificador del registro, de manera que nos quede de la siguiente manera:
2 Para asignar un nombre a un control lo que debemos hacer es sacar las propiedades de ese control e irnos a la Pestaña Otras >
Nombre. Ahí escribimos el nombre que queramos.

4
Visítame en http://neckkito.siliconproject.com.ar
Y para forzar a que la lista “relea” los valores cada vez que
nos cambia el [Id] (porque naveguemos por los registros,
por ejemplo) y, al mismo tiempo, forzar que, en cada
navegación de registro, el cuadro de lista inicialmente no
devuelva valor (importante para evitar el “efecto
memoria”), lo único que debemos hacer es sacar las
propiedades del formulario (ojo, del formulario) y Pestaña
Eventos > Al Activar Registro, y generamos el siguiente
código3:


Private Sub Form_Current()
Me.lstAdjuntos.Requery
Me.lstAdjuntos.Value = Null
End Sub

Y, con esto, ya tenemos nuestra preciosa lista con adjuntos

¿A que ha sido fácil? ;-)

AÑADIR ADJUNTOS A NUESTRO EXPEDIENTE


Para añadir adjuntos a nuestro expediente vamos a servirnos de un módulo que ya utilizamos
en el ejemplo “¡Quiero navegar y seleccionar un archivo!”. Así pues, o bien importamos el
módulo en el VBE (y lo tendremos que modificar ligeramente) o bien nos creamos un módulo
estándar4, lo guardamos como mdlSeleccionCarpetaArchivo, y escribimos el siguiente código
en él:
3 Para generar código debemos sacar las propiedades del control > Pestaña Eventos, y nos situamos en la parte “blanca” a la
derecha del evento que queremos programar. Veremos un pequeño botón de puntos suspensivos. Si hacemos clic sobre él nos
aparecerá una ventana que nos pedirá qué operación deseamos realizar. Le indicamos que queremos “generar código”.
4 Para insertar un módulo estándar podemos abrir el editor de VB (ALT+F11) y nos vamos a Menú > Insertar > Módulo

5
Visítame en http://neckkito.siliconproject.com.ar

Public Function buscaArchivo() As String
'Requiere referencia a Microsoft Office x.y Object Library, donde x.y es la versión que tengamos en nuestro PC.
Dim fDialog As Office.FileDialog
'Instanciamos el objeto fDialog
Set fDialog = Application.FileDialog(msoFileDialogFilePicker)
With fDialog
.AllowMultiSelect = False
.ButtonName = "Seleccionar"
.Title = "Seleccionar el archivo a adjuntar"
.InitialFileName = "C:\"
.InitialView = msoFileDialogViewDetails
.Filters.Clear
.Filters.Add "All Files", "*.*"
If .Show = True Then
buscaArchivo = .SelectedItems(1)
Else
'No hago nada, o puedo avisar si descomento la línea siguiente
'MsgBox "Ha pulsado el botón <Cancelar>."
End If
End With
End Function

Tened en cuenta que, para poderlo utilizar, es necesario registrar la referencia a la librería
“Microsoft Office x.y Object Library”, donde x.y es la versión de Office que tengamos instalada
en nuestro PC5.

Bueno… pues sigamos. Vamos a hacer una parada en nuestro formulario para añadir un botón
de comando, que llamaremos cmdAnadeAdjunto, y en su evento “Al hacer clic” le generaremos
el siguiente código:


Private Sub cmdAnadeAdjunto_Click()
Dim laRutaArchivo As String
Dim unId As Long

'Cogemos el identificador del registro. Si no lo hay salimos.


unId = Nz(Me.Id.Value, 0)
If unId = 0 Then Exit Sub

'Llamamos a la función para buscar el archivo


laRutaArchivo = buscaArchivo()
'Si no se ha seleccionado nada, o se cancela, no hacemos nada
If laRutaArchivo = "" Then
Exit Sub
Else
Call subAnadeAdjunto(laRutaArchivo, "TExpedientes", "ArchAdj", "Id", unId)
End If

'Refrescamos la información del listbox

5 Para registrar una referencia debemos irnos, en el editor de VB, a Menú > Herramientas > Referencias... Se nos abrirá una
ventana mostrándonos todas las referencias disponibles. Buscamos la que nos interese, marcamos su check y aceptamos.

6
Visítame en http://neckkito.siliconproject.com.ar
Me.lstAdjuntos.Requery
End Sub

Fijaos en lo que hace el código:

1.- Coge el identificador del registro a través de la variable


unId. Lógicamente, si no hay [Id] aún asignado no
podemos hacer nada, por lo que salimos del procedimiento
sin seguir la ejecución.

2.- Llamamos a la función (es decir, al fileDialog), para poder seleccionar el archivo que
queremos adjuntar. De nuevo, si cancelamos la función buscaArchivo() devuelve una cadena
vacía, con lo cual saldríamos del procedimiento.

3.- En caso contrario llamamos al procedimiento subAnadeAdjunto, pasándole como


parámetros:

 El archivo que hemos seleccionado (con su ruta completa)


 El nombre de la tabla sobre la que estamos trabajando
 El nombre del campo que contiene los archivos adjuntos
 El nombre del campo que recoge el identificador del registro
 El valor del identificador del registro en el que estamos trabajando.

4.- Realizado el proceso de inserción efectuamos un refresco de nuestro listbox para que nos
muestre el nuevo adjunto insertado.

Con todo lo indicado en el punto 3 vemos que podemos aplicar la llamada a cualquier
formulario, a cualquier tabla e independientemente de cómo hayamos llamado a nuestros
campos.

Como os comentaba al principio, el sistema se basa en la existencia de un identificador


inequívoco del registro, dado que es es uno de los argumentos que debemos pasar al
procedimiento.

Y como estamos metidos de lleno en la inserción de adjuntos continuaremos con lo que sería el
código del módulo que nos permitirá trabajar con adjuntos. Así que, ni cortos ni perezosos,
vamos a crearnos un nuevo módulo estándar que llamaremos mdlManejoAdjuntos.

Como vamos a realizar varias operaciones lo que haremos será, bajo la/s línea/s Option,
declarar las variables privadas comunes a todo el módulo, así;


'-----------------------------------------------------------------------------------------------------------------------
' Declaramos las variables comunes de todo el módulo
'-----------------------------------------------------------------------------------------------------------------------
Dim dbs As Database
Dim rst As Recordset2
Dim rstAdj As Recordset2
Dim fldCampoAdj As Field2
Dim miSql As String

Sigamos. A continuación insertaremos el procedimiento que nos permite añadir los adjuntos en
función de los parámetros que habíamos comentado en el punto 3 unas líneas más arriba:

7
Visítame en http://neckkito.siliconproject.com.ar

'-----------------------------------------------------------------------------------------------------------------------
' Sub subAnadeAdjunto(parametros): procedimiento que permite añadir adjuntos a un campo de una tabla
'-----------------------------------------------------------------------------------------------------------------------
Public Sub subAnadeAdjunto( _
elArchivo As String, _
laTabla As String, _
elCampoAdj As String, _
elCampoId As String, _
elId As Long)
On Error GoTo sol_err

'Instanciamos la base de datos


Set dbs = CurrentDb
'Creamos la sql
miSql = "SELECT [" & elCampoAdj & "] FROM [" & laTabla & "] WHERE [" & elCampoId & "]=" & elId
'Creamos el recordset sobre la Sql
Set rst = dbs.OpenRecordset(miSql)
'Instanciamos el campo del adjunto
Set fldCampoAdj = rst.Fields(0)
'Instaciamos el rstAdj
Set rstAdj = fldCampoAdj.Value

'Situamos el registro en modo edición


rst.Edit
'Añadimos el nuevo adjunto
rstAdj.AddNew
'Utilizamos el método LoadFromFile para cargar el archivo
rstAdj.FileData.LoadFromFile elArchivo
rstAdj.Update
rst.Update

Salida:
'Cerramos conexiones y liberamos memoria
rst.Close
dbs.Close

Set rst = Nothing


Set rstAdj = Nothing
Set dbs = Nothing
Exit Sub

sol_err:
If Err.Number = 3820 Then 'El adjunto ya existe
MsgBox "Ya existe un adjunto con el nombre y extensión que intenta adjuntar", _
vbExclamation, "DUPLICADO"
Else
MsgBox "Se ha producido el error " & Err.Number & " - " & Err.Description, _
vbCritical, "ERROR"
End If
Resume Salida
End Sub '-----------------------------------------------------------------------FIN subAnadeAdjunto

¿Qué me gustaría remarcar del código? Pues un par de cosillas:

8
Visítame en http://neckkito.siliconproject.com.ar
 Fijaos que declaro los recordsets y el campo como
Recordset2 y Field2. Ello es así porque este tipo de objetos
está especialmente preparado para poder trabajar con
campos multivalor y, por supuesto, con campos de datos
adjuntos.

 Fijaos que construyo la con consulta SQL (a través de la


variable miSql) prácticamente con los parámetros que le
pasamos al procedimiento. Ello me permite, además de
acotar un solo registro, poder utilizar índices en el
recordset. Es decir, utilizo el cero para referirme a la
colección fields que devuelve la SQL (Set fldCampoAdj =
rst.Fields(0)), dado que fuerzo a la SQL para que me devuelva
un solo campo, que es el campo de datos adjuntos.

 Fijaos que creo un recordset sobre el contenido del campo adjunto ( Set rstAdj =
fldCampoAdj.Value).
Es decir, y hablando en abstracto, es como si creara un recordset sobre un
recordset. Y lo hago sobre la propiedad value.

Quizá estos conceptos se vean un poco más claros en las siguientes acciones que
programaremos, donde nos veremos obligados a recorrer la colección de adjuntos. Así que, ¡no
desesperéis! ;-)

 Fijaos que para insertar el adjunto necesitamos:

✗ Situar el recordset rst en modo edición (rst.Edit)


✗ Preparar el recordset rstAdj para añadir el dato (rstAdj.AddNew)
✗ Cargar el archivo seleccionado (rstAdj.FileData.LoadFromFile elArchivo)
✗ Actualizar el recordset rstAdj (rstAdj.Update)
✗ Actualizar el recordset rst (rst.Update)

 Finalmente, fijaos que para cargar el adjunto hacemos referencia al subcampo “FileData”
(que ya conocemos al haber preparado la consulta que os explicaba más arriba), y que
utilizamos el método LoadFromFile (y ya veremos que para extraer adjuntos podremos utilizar
el método SaveToFile) para cargar el adjunto que queremos.

9
Visítame en http://neckkito.siliconproject.com.ar
ELIMINAR ADJUNTOS DE NUESTRO
EXPEDIENTE
Siguiendo con la tónica del ejemplo, veamos cómo podemos
borrar adjuntos de nuestro campo adjuntos empezando por
nuestro formulario.

Así pues vamos a crearnos un botón de comando en


FExpedientes, que llamaremos cmdBorraAdjunto. En su
evento “Al hacer clic” le generaremos el siguiente código:


Private Sub cmdBorraAdjunto_Click()
Dim nombreAdjunto As String
Dim resp As Integer
Dim unId As Long

'Miramos si existe identificador. Si no salimos sin hacer nada


unId = Nz(Me.Id.Value, 0)
If unId = 0 Then Exit Sub

'Cogemos el adjunto seleccionado en la lista. Si no hay valor seleccionado salimos


nombreAdjunto = Nz(Me.lstAdjuntos.Value, "")
If nombreAdjunto = "" Then Exit Sub

'Solicitamos confirmación de eliminación


resp = MsgBox("¿Realmente desea eliminar el archivo " & nombreAdjunto & "?", _
vbQuestion + vbYesNo, "CONFIRMACIÓN")
If resp = vbYes Then 'Si se confirma la eliminación...
Call subBorraAdjunto(nombreAdjunto, "TExpedientes", "ArchAdj", "Id", unId)
Me.lstAdjuntos.Requery
Me.lstAdjuntos.Value = Null
End If
End Sub

De nuevo vemos que, tras solicitar confirmación de la eliminación del adjunto seleccionado en
nuestro cuadro de lista, hacemos una llamada al procedimiento subBorraAdjunto(), al cual
pasamos como parámetros el nombre del adjunto seleccionado, la tabla de trabajo, el nombre
del campo que contiene los adjuntos, el nombre del campo que hayamos definido como
identificador y el identificador del registro en el que estamos situados.

Incidir que, tras borrar el archivo, refrescamos la información de nuestro listbox a través de el
forzado de una nueva lectura del origen de datos (Me.lstAdjuntos.Requery) y, además, y para evitar
el efecto memoria, establecemos el valor que devuelve la lista en NULL ( Me.lstAdjuntos.Value =
Null).

Alguien podría decirme: “¿Y qué es esto del “efecto memoria”? Pues bien: todas las
operaciones las estamos realizando “por detrás” a través de código. Supongamos que tenemos
un adjunto llamado “XXX.yyy”. Si realizamos la eliminación del mismo el archivo,
efectivamente, se elimina, y nuestro cuadro de lista ya no lo muestra. Si volvemos a pulsar el
botón de eliminar, ¿qué mensaje de confirmación nos saldrá? Pues nos pedirá, otra vez, si
queremos eliminar “XXX.yyy”, que ya no existe, y si lo intentamos borrar nos saltará un error
de código porque no se encuentra el adjunto que se quiere eliminar. Es decir, es “como” si el
listbox siguiera devolviéndonos el valor del último archivo seleccionado; esto es, “se acuerda”
(hablando en términos no muy técnicos… je, je…). Para evitar esto (y el consiguiente error de
código) es por lo que forzamos a nuestro listbox a devolver un NULL, y ya hemos visto en el
código que si no hay valor el código se interrumpe y no se ejecuta
( 'Cogemos el adjunto seleccionado en la lista. Si no hay valor seleccionado salimos
nombreAdjunto = Nz(Me.lstAdjuntos.Value, "")
If nombreAdjunto = "" Then Exit Sub)

10
Visítame en http://neckkito.siliconproject.com.ar
¿Y cómo realizamos la eliminación “efectiva”? Pues volvemos a nuestro módulo
mdlManejoAdjuntos y escribimos el siguiente procedimiento:


'-----------------------------------------------------------------------------------------------------------------------
' Sub subBorraAdjunto(parametros): procedimiento que permite borrar adjuntos de un campo de una tabla
'-----------------------------------------------------------------------------------------------------------------------
Public Sub subBorraAdjunto( _
elAdjunto As String, _
laTabla As String, _
elCampoAdj As String, _
elCampoId As String, _
elId As Long)
On Error GoTo sol_err

'Instanciamos la base de datos


Set dbs = CurrentDb
'Creamos la sql
miSql = "SELECT [" & elCampoAdj & "] FROM [" & laTabla & "] WHERE [" & elCampoId & "]=" & elId
'Creamos el recordset sobre la Sql
Set rst = dbs.OpenRecordset(miSql)
'Instanciamos el campo del adjunto
Set fldCampoAdj = rst.Fields(0)
'Instaciamos el rstAdj
Set rstAdj = fldCampoAdj.Value

'Recorremos los adjuntos hasta encontrar el que queremos borrar


With rstAdj
.MoveFirst
Do Until .EOF
If .FileName = elAdjunto Then
.Delete
MsgBox "Adjunto borrado correctamente", vbInformation, "CORRECTO"
Exit Do
End If
.MoveNext
Loop
End With
Salida:
'Cerramos conexiones y liberamos memoria
rst.Close
dbs.Close

Set rst = Nothing


Set rstAdj = Nothing
Set dbs = Nothing
Exit Sub

sol_err:
MsgBox "Se ha producido el error " & Err.Number & " - " & Err.Description _
& vbcrlf & "en el procedimiento mdlManejoAdjuntos > subBorraAdjunto()", _
vbCritical, "ERROR"
Resume Salida
End Sub '-----------------------------------------------------------------------FIN subBorraAdjunto

La mayoría de comentarios que realizaba en la adición del adjunto nos sirven para este código.

11
Visítame en http://neckkito.siliconproject.com.ar
Solamente destacar que, en este caso, sí realizo un
recorrido por la colección de adjuntos ( Do Until .EOF) y voy
mirando si coincide el nombre del adjunto examinado (a
través del valor devuelto por el subcampo que ya
conocemos “FileName”) con el del adjunto seleccionado
(If .FileName = elAdjunto Then). Y, cuando coinciden… ¡zas! Me lo
cargo al estilo Terminator (Sayonara, baby). :-)

EXTRAER UN ADJUNTO AL DISCO DURO Y


VISUALIZARLO (O NO)
Lanzados como vamos, veamos cómo extraer el archivo
adjunto que hayamos seleccionado. Y, por seguir siendo
originales, empezaremos con nuestro formulario.

Así pues, insertaremos un botón de comando que llamaremos cmdExtraeAdjunto, y en su


evento “Al hacer clic” le generaremos el siguiente código:


Private Sub cmdExtraeAdjunto_Click()
Dim nombreAdjunto As String
Dim unaRuta As String
Dim resp As Integer
Dim unId As Long

'Miramos si existe identificador. Si no salimos sin hacer nada


unId = Nz(Me.Id.Value, 0)
If unId = 0 Then Exit Sub

'Cogemos el adjunto seleccionado en la lista. Si no hay valor seleccionado salimos


nombreAdjunto = Nz(Me.lstAdjuntos.Value, "")
If nombreAdjunto = "" Then Exit Sub

'Llamamos a la función buscaCarpeta para que el usuario pueda seleccionar una ruta de extracción
'Si se cancela la función devuelve una cadena vacía. Si es así salimos
unaRuta = buscaCarpeta()
If unaRuta = "" Then Exit Sub

'Extraemos el archivo a través de la llamada al procedimiento subExtraeAdjunto


Call subExtraeAdjunto(unaRuta, nombreAdjunto, "TExpedientes", "ArchAdj", "Id", Me.Id)

'Pedimos al usuario si quiere ver el archivo una vez extraído


resp = MsgBox("¿Desea visualizar el archivo extraído?", vbQuestion + vbYesNo, "CONFIRMACIÓN")
If resp = vbYes Then 'Si sí queremos visualizarlo...
Application.FollowHyperlink unaRuta & nombreAdjunto
End If
End Sub

¿Qué nos encontramos aquí de nuevo?

Pues, en primer lugar, vemos que permitimos al usuario elegir la carpeta de destino a través
de la llamada a la función buscaCarpeta()

En segundo lugar, observemos que hemos tenido que añadir un parámetro nuevo a nuestro
procedimiento subExtraeAdjunto(), que es la ruta donde queremos guardar el archivo
(representada por la variable unaRuta).

Pues, ¿qué mejor que ir por orden? Nos situamos en nuestro módulo
mdlSeleccionCarpetaArchivo y añadimos una nueva función pública a través del siguiente
código:

12
Visítame en http://neckkito.siliconproject.com.ar

Public Function buscaCarpeta() As String
'Requiere referencia a Microsoft Office x.y Object Library, donde x.y es la versión que tengamos en nuestro PC.
Dim fDialog As Office.FileDialog
'Instanciamos el objeto fDialog
Set fDialog = Application.FileDialog(msoFileDialogFolderPicker)
With fDialog
.AllowMultiSelect = False
.ButtonName = "Seleccionar"
.Title = "Seleccionar la carpeta de destino"
.InitialFileName = "C:\"
.InitialView = msoFileDialogViewDetails
If .Show = True Then
buscaCarpeta = .SelectedItems(1) & "\"
Else
'No hago nada, o puedo avisar si descomento la línea siguiente
'MsgBox "Ha pulsado el botón <Cancelar>."
End If
End With
End Function

Fijaos, en relación a nuestra función para seleccionar un archivo, que ahora nuestro FileDialog
utiliza la constante adecuada para seleccionar una carpeta ( msoFileDialogFolderPicker), con lo que
ya no necesitamos filtros de ninguna clase (hemos eliminado las líneas que contenían las
órdenes relativas a “Filters”).

Y, por fin, echemos un vistazo a nuestro módulo mdlManejoAdjuntos y añadamos el


procedimiento adecuado para la extracción de adjuntos, que es el siguiente:


'-----------------------------------------------------------------------------------------------------------------------
' Sub subExtraeAdjunto(parametros): procedimiento que permite extraer adjuntos de un campo de una tabla
'-----------------------------------------------------------------------------------------------------------------------
Public Sub subExtraeAdjunto( _
laRuta As String, _
elAdjunto As String, _
laTabla As String, _
elCampoAdj As String, _
elCampoId As String, _
elId As Long)
On Error GoTo sol_err

'Instanciamos la base de datos


Set dbs = CurrentDb
'Creamos la sql
miSql = "SELECT [" & elCampoAdj & "] FROM [" & laTabla & "] WHERE [" & elCampoId & "]=" & elId
'Creamos el recordset sobre la Sql
Set rst = dbs.OpenRecordset(miSql)
'Instanciamos el campo del adjunto
Set fldCampoAdj = rst.Fields(0)
'Instaciamos el rstAdj
Set rstAdj = fldCampoAdj.Value

13
Visítame en http://neckkito.siliconproject.com.ar
'Recorremos los adjuntos hasta encontrar el que queremos extraer, y lo
extraemos en la ruta seleccionada
With rstAdj
.MoveFirst
Do Until .EOF
If .FileName = elAdjunto Then
.FileData.SaveToFile laRuta
MsgBox "Archivo extraído correctamente", vbInformation, "CORRECTO"
Exit Do
End If
.MoveNext
Loop
End With
Salida:

'Cerramos conexiones y liberamos memoria


rst.Close
dbs.Close

Set rst = Nothing


Set rstAdj = Nothing
Set dbs = Nothing
Exit Sub

sol_err:
MsgBox "Se ha producido el error " & Err.Number & " - " & Err.Description _
& vbCrLf & "en el procedimiento mdlManejoAdjuntos > subExtraeAdjunto()", _
vbCritical, "ERROR"
Resume Salida
End Sub '-----------------------------------------------------------------------FIN subExtraeAdjunto

Creo que, después de todo lo aprendido, no hace falta comentar este último código. Sólo
remarcar como elemento novedoso que, para guardar el archivo extraído utilizamos el método
“SaveToFile” (.FileData.SaveToFile laRuta) indicándole la ruta donde queremos salvarlo, pero, ojo,
sin indicar en esa ruta el nombre y extensión de archivo.

EXTRAER TODOS LOS ADJUNTOS “DE GOLPE”


Con lo explicado hasta ahora deberíais ser capaces de programar una rutina que extrajera
todos los adjuntos de una sola tacada. Sin embargo, como estoy “inspirado”, os añadiré un
procedimiento para poder hacer eso.

En nuestro formulario añadimos un botón de comando que llamaremos


cmdExtraeTodosAdjuntos, y en su evento “Al hacer clic” le generaremos el siguiente código:


Private Sub cmdExtraeTodosAdjuntos_Click()
Dim unaRuta As String
Dim unId As Long

'Miramos si existe identificador. Si no salimos sin hacer nada


unId = Nz(Me.Id.Value, 0)
If unId = 0 Then Exit Sub

'Llamamos a la función buscaCarpeta para que el usuario pueda seleccionar una ruta de extracción
'Si se cancela la función devuelve una cadena vacía. Si es así salimos
unaRuta = buscaCarpeta()
If unaRuta = "" Then Exit Sub

'Extraemos el archivo a través de la llamada al procedimiento subExtraeTodosAdjuntos


Call subExtraeTodosAdjuntos(unaRuta, "TExpedientes", "ArchAdj", "Id", Me.Id)
End Sub

14
Visítame en http://neckkito.siliconproject.com.ar
Y en nuestro módulo mdlManejoAdjuntos nos creamos un procedimiento como el que sigue:


'-----------------------------------------------------------------------------------------------------------------------
' Sub subExtraeTodosAdjuntos(parametros): procedimiento que permite extraer todos los adjuntos de un campo de
una tabla
'-----------------------------------------------------------------------------------------------------------------------
Public Sub subExtraeTodosAdjuntos( _
laRuta As String, _
laTabla As String, _
elCampoAdj As String, _
elCampoId As String, _
elId As Long)
On Error GoTo sol_err

'Instanciamos la base de datos


Set dbs = CurrentDb
'Creamos la sql
miSql = "SELECT [" & elCampoAdj & "] FROM [" & laTabla & "] WHERE [" & elCampoId & "]=" & elId
'Creamos el recordset sobre la Sql
Set rst = dbs.OpenRecordset(miSql)
'Instanciamos el campo del adjunto
Set fldCampoAdj = rst.Fields(0)
'Instaciamos el rstAdj
Set rstAdj = fldCampoAdj.Value

'Si no hay ningún adjunto avisamos y salimos


If rstAdj.RecordCount = 0 Then
MsgBox "No hay adjuntos para extraer", vbExclamation, "SIN ADJUNTOS"
GoTo Salida
End If

'Recorremos los adjuntos y los vamos extrayendo en la ruta seleccionada


With rstAdj
.MoveFirst
Do Until .EOF
.FileData.SaveToFile laRuta
.MoveNext
Loop
End With

MsgBox "Archivos extraídos correctamente", vbInformation, "CORRECTO"

Salida:
'Cerramos conexiones y liberamos memoria
rst.Close
dbs.Close

Set rst = Nothing


Set rstAdj = Nothing
Set dbs = Nothing
Exit Sub

sol_err:
MsgBox "Se ha producido el error " & Err.Number & " - " & Err.Description _
& vbCrLf & "en el procedimiento mdlManejoAdjuntos > subExtraeTodosAdjuntos()", _
vbCritical, "ERROR"
Resume Salida
End Sub '-----------------------------------------------------------------------FIN subExtraeTodosAdjuntos

15
Visítame en http://neckkito.siliconproject.com.ar

Y, con esto, a otra cosa, mariposa.

UN ÚLTIMO APUNTE
Soy consciente de que hay partes de código que pueden
mejorarse o, mejor dicho, optimizarse. Igualmente soy
consciente de que se podría mejorar la parte de control de
errores. Lo sé, lo sé… no hace falta que me lo
recordéis… ;-)

Sin embargo, me he decidido a dejar las cosas así como están porque, en realidad, mi objetivo
es que entendáis una a una las acciones que hemos explicado en relación al tratamiento de
adjuntos. En aras de ese objetivo he querido sacrificar cierta “pulcritud” en el código. Supongo
que me perdonaréis esta pequeña licencia, ¿verdad? :D

PARA FINALIZAR EL EJEMPLO


Pues bueno… al final me ha quedado un ejemplo un tanto “intenso”, pero creo que explicar
cómo manejar archivos adjuntos a través de código VBA sin explicar las opciones y acciones
más comunes que podemos realizar con los adjuntos era quedarse un poco cojo.

Ahora mismo puedo pensar en operaciones no explicadas y tal vez no tan comunes pero que,
quizá, en algún momento, pudiéramos requerir. Por ejemplo, ¿cómo copiaríamos un registro
completo a otra tabla?

Pues en ese caso se me ocurren dos posibilidades: o bien utilizar única y exclusivamente
recordsets, o bien realizar dos operaciones: la primera a través de una SQL de inserción de
datos, dejando de lado los adjuntos, y a continuación, y a través de un identificador, utilizar un
conjunto de recordsets para ir escribiendo, de uno a otro, la información de los adjuntos
(recordemos, sus tres subcampos: “FileData”, “FileName” y “FileType”). Eso es algo que no he
probado y que, ahora mismo, no podría definiros más claramente (¿funcionaría? Gran
pregunta… ).

Finalmente, espero que el ejemplo os pueda ser útil. Un saludo, y…

¡suerte!

16
Visítame en http://neckkito.siliconproject.com.ar

S-ar putea să vă placă și