aboutsummaryrefslogtreecommitdiff
path: root/files/es/vigilar_descargas/index.html
blob: b2e11604527ce6fa0bbd8cc94a65e8b5a87378ed (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
---
title: Vigilar descargas
slug: Vigilar_descargas
tags:
  - Download Manager
  - Firefox 3
translation_of: Archive/Mozilla/Monitoring_downloads
---
<p> Con Firefox 3 es más fácil que nunca vigilar el estado de las descargas. Aunque ya era posible hacer esto en versiones previas de Firefox, solo era posible hacer posible tener un observador en cada momento. Firefox 3 introduce un nuevo API que permite cualquier numero de observadores para las descargas.</p>
<p>Este articulo demuestra como vigilar las descargas en Firefox 3, usando el administrador de descargas. Como un bonito añadido adicional, demuestra demás cómo usar el API <a href="/es/Storage" title="es/Storage">Storage</a> para crear instrucciones <a class="external" href="http://www.sqlite.org/">sqlite</a> en una base de datos. El resultado es una ventana que puedes abrir seleccionando "Registro de descargas" en el menú herramientas, que muestra todas las descargas que se han iniciado desde que se instaló la extensión. En la lista está el nombre del archivo, las horas de comienzo y final de la descarga, la velocidad de descarga y el estado de la descarga. Se incluye un título emergente que muestra la URL completa de la fuente del archivo.</p>
<p>Descarga un ejemplo completo en <a class="external" href="/samples/extension-samples/DownloadLogger.zip" title="samples/extension-samples/DownloadLogger.zip">samples/extension-samples/DownloadLogger.zip</a></p>
<h3 id="Configuraci.C3.B3n" name="Configuraci.C3.B3n">Configuración</h3>
<p>Al cargar la extensión, se realizarán algunas tareas de limpieza rutinaria. En particular, necesita que exista una versión de la interfaz del administrador de descargas <code><a href="/es/nsIDownloadManager" title="es/nsIDownloadManager">nsIDownloadManager</a></code> y crea una base de datos en donde almacenará sus datos.</p>
<p> </p>
<pre class="eval"> onLoad: function() {
   // código de inicialización
   this.initialized = true;
   this.strings = document.getElementById("downloadlogger-strings");

   this.dlMgr = Components.classes["@mozilla.org/download-manager;1"]
                          .getService(Components.interfaces.nsIDownloadManager);

   this.dlMgr.addListener(downloadlogger);

   // Abre la base de datos, poniendo su archivo en el directorio de perfil (profile)

   this.dbFile = Components.classes["@mozilla.org/file/directory_service;1"]
                    .getService(Components.interfaces.nsIProperties)
                    .get("ProfD", Components.interfaces.nsIFile);
   this.dbFile.append("downloadlogger.sqlite");

   // Accede al servicio de almacenamiento y abre la base de datos

   this.storageService = Components.classes["@mozilla.org/storage/service;1"]
                       .getService(Components.interfaces.mozIStorageService);

   var dbConn = this.storageService.openDatabase(this.dbFile);

   // Ahora crea la tabla; si ya existe, esto produce un error, pero ¡no importa!

   dbConn.executeSimpleSQL("CREATE TABLE items (source TEXT, size INTEGER," +
                           " startTime INTEGER, endTime INTEGER," +
                           " speed REAL, status INTEGER)");
   dbConn.close();
 },
</pre>
<p>Esto es bastante simple. El administrador de descargas es almacenado en una variable en el objeto <code>downloadlogger</code> para ser usado más tarde y llama a su método <code>addListener()</code> para empezar la escucha de los cambios de estado de descargas. El archivo de la base de datos se abre y se ejecuta la instrucción sqlite: <code>CREATE TABLE</code> para crear la tabla.</p>
<p>Finalmente, se cierra la base de datos.</p>
<div class="note"><strong>Nota:</strong> El método <code>close()</code> de <code><a href="/es/mozIStorageConnection" title="es/mozIStorageConnection">mozIStorageConnection</a></code> se añade en la versión alpha 8 de Firefox 3. En versiones anteriores de Firefox, no hay forma explícita de cerrar la base de datos; Se cierra cuando el colector de desperdicios desecha el objeto conexión.</div>
<h3 id="Manejando_los_cambios_de_estados_de_las_descargas" name="Manejando_los_cambios_de_estados_de_las_descargas">Seguir los cambios de estados de las descargas</h3>
<p>Una vez que el código mostrado más arriba se ejecuta, cada vez que hay un cambio de estado se produce una llamada a nuestro método <code>onDownloadStateChange()</code>. Esto es parte de la interfaz <code><a href="/es/nsIDownloadProgressListener" title="es/nsIDownloadProgressListener">nsIDownloadProgressListener</a></code>.</p>
<p>Ese código es parecido a esto:</p>
<pre class="eval"> onDownloadStateChange: function(aState, aDownload) {
   var statement;

   switch(aDownload.state) {
     case Components.interfaces.nsIDownloadManager.DOWNLOAD_DOWNLOADING:

       // Agregar una fila para la nueva descarga que comienza; cada fila incluye
       // la URI del origen, tamaño y hora de comienzo. La hora de final y la velocidad de descarga
       // se ponen ambas a cero ya que no las conocemos aún.

       // status es el valor del estado llegado desde el administrador de descargas.

       var dbConn = this.storageService.openDatabase(this.dbFile);
       statement = dbConn.createStatement("REPLACE INTO items VALUES " +
                                          "(?1, ?2, ?3, 0, 0.0, 0)");

       statement.bindStringParameter(0, aDownload.source.spec);
       statement.bindInt64Parameter(1, aDownload.size);
       statement.bindInt64Parameter(2, aDownload.startTime);
       statement.execute();
       statement.reset();
       dbConn.close();
       break;

     // Registra si se ha completado (o ha fallado) la descarga

     case Components.interfaces.nsIDownloadManager.DOWNLOAD_FINISHED:
     case Components.interfaces.nsIDownloadManager.DOWNLOAD_FAILED:
     case Components.interfaces.nsIDownloadManager.DOWNLOAD_CANCELED:
       this.logTransferCompleted(aDownload);
       break;
   }
 },
</pre>
<p>Nos interesan cuatro estados. Si el estado de la descarga, indicado por el campo <code>aDownload.state</code> es <code>Components.interfaces.nsIDownloadManager.DOWNLOAD_DOWNLOADING</code>, se ha comenzado la descarga del archivo. El objeto <code>aDownload</code> es un objeto <code><a href="/es/nsIDownload" title="es/nsIDownload">nsIDownload</a></code>.</p>
<p>En este caso, creamos una nueva fila en nuestra base de datos para el nuevo archivo, abriendo la base de datos y construyendo una instrucción sqlite: <code>REPLACE INTO</code>. Las primeras tres filas se comlpetan con el valor de la URI de origen, tamaño del archivo y hora de comienzo del objeto descarga (<em>download</em>). Las demás filas se ponen a cero ya que esa información no la tenemos ahora.</p>
<p>Si el estado de la descarga indica que ésta ha terminado, se ha cancelado o ha fallado, llamamos a nuestra rutina <code>logTransferCompleted</code> para actualizar el registro indicando el cambio de estado. El código es como:</p>
<pre class="eval"> logTransferCompleted: function(aDownload) {
     var endTime = new Date();                // La hora actual es la hora de final

     // Utiliza la instrucción sqlite REPLACE para actualizar el registro. Encontramos un
     // registro para cada URI y hora de comienzo y actualizamos la hora de final,
     // tamaño y velocidad en el registro. Haciendo coincidir la URI y la hora de comienzo,
     // podemos tener varias entradas de múltiples descargas del mismo archivo.
</pre>
<p> </p>
<pre class="eval">     var dbConn = this.storageService.openDatabase(this.dbFile);
     var statement = dbConn.createStatement("UPDATE items SET size=?1, " +
         "endTime=?2, speed=?3, status=?4 WHERE source=?5 and startTime=?6");
     statement.bindInt64Parameter(0, aDownload.size);
     statement.bindInt64Parameter(1, endTime.getTime());
     statement.bindDoubleParameter(2, aDownload.speed);
     statement.bindInt32Parameter(3, aDownload.state);
     statement.bindStringParameter(4, aDownload.source.spec);
     statement.bindInt64Parameter(5, aDownload.startTime);
     statement.execute();
     statement.reset();
     dbConn.close();
 },
</pre>
<p>Esto simplemente abre la base de datos y ejecuta la instrucción sqlite: <code>UPDATE</code> que encuentra la descarga cuya URI de origen y su hora de inicio coinciden con la descarga que se ha completado y actualiza su información. Buscando por un registro con igual URI y hora de comienzo, implementamos la posibilidad de que un usuario descargue el mismo archivo varias veces.</p>
<p> </p>
<h3 id="Mostrando_el_registro_de_descargas" name="Mostrando_el_registro_de_descargas">Mostrando el registro de descargas</h3>
<p>El código de la ventana de registro está encapsulado en un objeto llamado <code>downloadlogger_dlwindow</code>. Ya que este es un ejemplo simple, es una ventana de registro de una única descarga; no nos preocupamos de cambios posteriores del registro. Simplemente mostramos el estado de la descarga en el momento en que la ventana se abre.</p>
<p>Esto significa que todo el trabajo puede hacerse en el manejador del evento de carga, que es algo así:</p>
<pre class="eval"> onLoad: function() {
   // Abre la base de datos

   this.dbFile = Components.classes["@mozilla.org/file/directory_service;1"]
                    .getService(Components.interfaces.nsIProperties)
                    .get("ProfD", Components.interfaces.nsIFile);
   this.dbFile.append("downloadlogger.sqlite");

   // Consigue acceso al servicio de almacenamiento y abre la base de datos

   this.storageService = Components.classes["@mozilla.org/storage/service;1"]
                       .getService(Components.interfaces.mozIStorageService);

   var dbConn = this.storageService.openDatabase(this.dbFile);

   var loglist = document.getElementById("loglist");

   var statement = dbConn.createStatement("SELECT * FROM items");   // Get all items in table
   try {
     while (statement.executeStep()) {
       var row = document.createElement('listitem');

       // Agrega las celdas al registro (fila de la tabla)

       var cell = document.createElement('listcell');
       var sourceStr = statement.getString(0);
       row.setAttribute("tooltiptext", sourceStr);
       sourceStr = sourceStr.slice(sourceStr.lastIndexOf("/")+1, sourceStr.length);
       cell.setAttribute("label", sourceStr);   // Source
       row.appendChild(cell);

       cell = document.createElement('listcell');
       cell.setAttribute("label", (statement.getInt64(1) / 1024).toFixed(1) + "KB");    // Size
       cell.setAttribute("style", "text-align:right");
       row.appendChild(cell);

       var theDate = new Date(statement.getInt64(2) / 1000);        // Start time
       cell = document.createElement('listcell');
       var dateStr = theDate.toLocaleString();
       cell.setAttribute("label", dateStr);
       row.appendChild(cell);

       theDate = new Date(statement.getInt64(3));            // End time
       cell = document.createElement('listcell');
       dateStr = theDate.toLocaleString();
       cell.setAttribute("label", dateStr);
       row.appendChild(cell);

       var speed = statement.getDouble(4) / 1024.0;
       cell = document.createElement('listcell');
       cell.setAttribute("label", speed.toFixed(1) + "KB/sec");
       cell.setAttribute("style", "text-align:right");
       row.appendChild(cell);

       var status = statement.getInt32(5);
       var style = "color:black";
       cell = document.createElement('listcell');

       var statusStr;

       switch(status) {
         case 0:
           statusStr = "Descargando";
           break;
         case 1:
           statusStr = "Completada";
           style = "color:green";
           break;
         case 2:
           statusStr = "Fallida";
           style = "color:red";
           break;
         case 3:
           statusStr = "Cancelada";
           style = "color:purple";
           break;
         case 4:
           statusStr = "Pausada";
           style = "color:blue";
           break;
         case 5:
           statusStr = "En cola";
           style = "color:teal";
           break;
         case 6:
           statusStr = "Bloqueada";
           style = "color:white background-color:red";
           break;
         case 7:
           statusStr = "Escaneando";
           style = "color:silver";
           break;
         default:
           statusStr = "Desconocido";
           break;
       }
       cell.setAttribute("label", statusStr);
       cell.setAttribute("style", style);
       row.appendChild(cell);

       loglist.appendChild(row);
     }
   } finally {
     statement.reset();
     dbConn = null;
   }
 }
</pre>
<p>Este código es bastante simple. Comienza abriendo la base de datos sqlite que contiene la información de registro y entonces crea una sentencia SQL: <code>SELECT</code> para leer las entradas de la base de datos.</p>
<p>Para reiterar sobre los resultados, usamos un bucle <code>while</code> que llama al método <code>executeStep()</code> del objeto <code><a href="/es/MozIStorageStatement" title="es/MozIStorageStatement">mozIStorageStatement</a></code>. Cada vez que este método es llamado, se recoge una fila de resultados.</p>
<p>Después de esto, se crea el objeto fila y cada entrada del resultado de la búsqueda, se pone en su respectiva celda.</p>
<p>Los puntos a resaltar son:</p>
<ul> <li><code><a href="/es/MozIStorageStatement" title="es/MozIStorageStatement">mozIStorageStatement</a></code> tiene varias rutinas para recoger datos de los resultados de la búsqueda, incluyendo <code>getString()</code>, <code>getDouble()</code>, y <code>getInt64()</code>. Estos métodos toman como parámetro el indice en base cero de la columna cuyo valor queremos recuperar.</li>
</ul>
<ul> <li>Notese que la hora de comienzo está dividida por 1000 antes de crear el objeto JavaScript <code>Date</code> para ello. Eso es, ajustar el valor almacenado en la base de datos al que espera JavaScript.</li>
</ul>
<ul> <li>Para justificar a la derecha los valores numéricos de las columnas, ajustamos el atributo <code>style</code> de la celda apropiada a <code>text-align:right</code>.</li>
</ul>
<h3 id="Ejercicios_para_el_lector" name="Ejercicios_para_el_lector">Ejercicios para el lector</h3>
<p>Hay algunas cosas obvias que podrían hacerse para mejorar esta extensión. Si estás aprendiendo a usar el gestor de descargas o el API Storage, hay algunas cosas que convendría que miraras para practicar:</p>
<ul> <li>Añade código para actualizar la ventana del registro de descargas en vivo, en lugar de generar una lista estática cuando se abre por primera vez.</li>
</ul>
<ul> <li>Añade estadísticas adicionales. ¿Cuál es la velocidad media entre todas las descargas? ¿A qué hora del día obtienes el mayor rendimiento de descargas?</li>
</ul>
<ul> <li>Añade botones para borrar registros de la lista o para borrar todos los registros de descargas completadas.</li>
</ul>
<ul> <li>Añade búsquedas.</li>
</ul>
<h3 id="Ver_tambi.C3.A9n" name="Ver_tambi.C3.A9n">Ver también</h3>
<p><a href="/es/Storage" title="es/Storage">Storage</a>, <code><a href="/es/nsIDownloadManager" title="es/nsIDownloadManager">nsIDownloadManager</a></code>, <code><a href="/es/nsIDownload" title="es/nsIDownload">nsIDownload</a></code>, <code><a href="/es/nsIDownloadProgressListener" title="es/nsIDownloadProgressListener">nsIDownloadProgressListener</a></code></p> <p></p>