Un endpoint de carga recibe de un cliente un .xlsx de 180 MB, y la única pregunta que el código de entrada necesita responder es si el libro contiene una hoja llamada "Mapping". Abrir por completo ese archivo significa parsear la tabla de cadenas compartidas, la parte de estilos y el XML de cada hoja: cientos de megabytes de asignaciones y segundos de CPU de dos dígitos para leer una lista que vive en una sola parte XML pequeña. HotXLS, la biblioteca nativa de hojas de cálculo de losLab para Delphi, expone esa lista directamente: GetSheetNames devuelve los nombres de hojas, en el orden del libro, sin materializar una sola celda. Usada como puerta de entrada, convierte "abrir todo y ver" en una decisión de milisegundos.
Dónde viven realmente los nombres de hoja en cada formato
Ambos formatos de hoja de cálculo fueron diseñados con su tabla de contenido al inicio. Un paquete OOXML mantiene el catálogo de hojas en xl/workbook.xml, una parte que permanece en unos pocos kilobytes ya sea que el libro tenga diez filas o diez millones. Un .xls BIFF8 almacena registros BoundSheet cerca del inicio del flujo global del libro, antes de cualquier dato de celda. Un Open completo ignora ese regalo: infla la tabla de cadenas compartidas, decodifica los registros de estilo y recorre cada subflujo de hoja, porque debe asumir que ustedes tocarán cualquier celda. Una llamada de listado lee solo el catálogo y se detiene.
Vale la pena cuantificar la asimetría cuando defienden la ruta de código adicional: en un libro grande, la diferencia entre listar y cargar es de tres a cuatro órdenes de magnitud tanto en bytes tocados como en asignaciones realizadas, y a diferencia de una carga completa, el costo del listado no crece con el conteo de filas.
Una llamada, cuatro formatos de contenedor
TXLSWorkbook.GetSheetNames en la fachada XLS acepta no solo .xls, sino también .xlsx, .xlsm, .xltx y .xltm basados en zip, donde lee únicamente workbook.xml del archivo. Para entrada .xls, escanea registros BoundSheet y se detiene en el primer registro EOF del subflujo global, así que incluso un archivo binario muy grande cuesta solo sus primeros kilobytes. En la fachada XLSX, TXLSXWorkbook.GetSheetNames está documentado con una garantía que importa para código de servicio: la instancia del libro no se reinicia ni se llena por la llamada, de modo que una instancia que ya contiene un documento abierto puede sondear otros archivos sin riesgo. GetODSSheetNames extiende la misma idea a paquetes OpenDocument, y las sobrecargas de stream de todas estas llamadas permiten sondear una carga que nunca toca disco.
var
Book: TXLSXWorkbook;
Names: TStringList;
I: Integer;
begin
Names := TStringList.Create;
Book := TXLSXWorkbook.Create;
try
if Book.GetSheetNames('upload-7f3a.xlsx', Names) <= 0 then
raise Exception.Create('unreadable workbook package');
if Names.IndexOf('Mapping') < 0 then
raise Exception.Create('required Mapping sheet is missing');
for I := 0 to Names.Count - 1 do
Writeln(Format('sheet %d: %s', [I, Names[I]]));
finally
Book.Free;
Names.Free;
end;
end;
La misma llamada respalda muy bien un diálogo de importación de escritorio: listar las hojas, dejar que el usuario elija una y solo entonces pagar el costo de una apertura completa. Los usuarios con libros de cincuenta hojas notan la diferencia entre un selector que aparece al instante y uno que aparece después de cargar todo el archivo.
La cobertura de extensiones también tiene un beneficio operativo discreto: los archivos .xlsm con macros y los formatos de plantilla se listan exactamente igual que un .xlsx normal, porque el catálogo de hojas vive en la misma parte workbook.xml sin importar si un vbaProject.bin viaja dentro del paquete. Por lo tanto, una canalización de entrada puede enumerar las hojas de un libro con macros para fines de enrutamiento sin tocar, ni ejecutar nada relacionado con, la carga de macros, y aplazar la política de macros a la etapa que realmente abre el archivo.
Leer el valor de retorno sin engañarse
Las convenciones de retorno de HotXLS no son uniformes en toda la biblioteca: algunas llamadas devuelven 1 para éxito y otras devuelven conteos, así que la verificación robusta para las funciones de listado es que <= 0 significa falla, en cuyo caso la lista de cadenas se ha vaciado. No traten una lista vacía como "libro sin hojas": tanto ECMA-376 como la especificación BIFF8 exigen al menos una hoja en un libro válido, así que cero nombres siempre es la ruta de falla, nunca un éxito degenerado.
La falla aquí también es una señal útil por sí misma. Un archivo con extensión .xlsx que falla en la llamada de listado está truncado, no es realmente un paquete OOXML (las exportaciones CSV mal rotuladas desde otros sistemas son una fuente permanente), o es un contenedor cifrado; y distinguir esos casos es exactamente para lo que sirve la siguiente comprobación. Registrar los primeros bytes del archivo fallido junto con el rechazo convierte la conversación de soporte en un intercambio de un mensaje en vez de un juego de adivinanzas.
Contenedores cifrados: detectar, no adivinar
Un .xlsx cifrado no es un zip. Según la especificación de criptografía de Microsoft Office, es un archivo compuesto OLE que envuelve los streams EncryptionInfo y EncryptedPackage, lo que significa que GetSheetNames no puede ver dentro y simplemente fallará. CanReadEncrypted sondea esa forma de contenedor para que la entrada pueda enrutar el archivo deliberadamente en lugar de registrar un error genérico de lectura desde lo profundo de un worker:
type
TIntakeRoute = (irNormal, irNeedsPassword, irUnreadable);
function ClassifyUpload(const FileName: string; Names: TStrings): TIntakeRoute;
var
Book: TXLSXWorkbook;
begin
Book := TXLSXWorkbook.Create;
try
// Encrypted OOXML is an OLE container, not a zip: check first,
// because the listing calls cannot look inside it.
if Book.CanReadEncrypted(FileName) then
Exit(irNeedsPassword);
if SameText(ExtractFileExt(FileName), '.ods') then
begin
if Book.GetODSSheetNames(FileName, Names) <= 0 then
Exit(irUnreadable);
end
else if Book.GetSheetNames(FileName, Names) <= 0 then
Exit(irUnreadable);
Result := irNormal;
finally
Book.Free;
end;
end;
Enrutar archivos cifrados requiere precisión, porque HotXLS es asimétrico aquí. El cifrado .xls heredado (RC4, RC4 CryptoAPI, XOR) es legible: TXLSWorkbook.Open(FileName, Password) descifra con una contraseña almacenada, así que esos archivos pueden permanecer en la ruta automatizada. Los paquetes OOXML cifrados, en cambio, pueden ser producidos por HotXLS pero no leídos de regreso: OpenEncrypted lanza EXlsxEncryptionNotImplemented para un paquete cifrado. El diseño honesto de entrada envía los .xlsx cifrados a una persona con Excel, y los .xls con contraseña al código.
Vale la pena señalarlo para operadores de lotes: el clasificador anterior puede correr cómodamente sobre todo un directorio entrante antes de que cualquier worker empiece procesamiento real, porque cada sondeo cuesta aproximadamente una apertura de archivo y unos pocos kilobytes de lectura. Adelantar la clasificación convierte "el lote de las 3 a.m. murió en el archivo 412 de 600" en "412 archivos en cola, 5 rechazados en entrada con motivos"; las mismas llamadas de biblioteca, una historia operativa muy distinta.
Lo que el sondeo ligero no puede decirles
Nombres y orden son toda la carga útil. Las llamadas de listado no reportan visibilidad (las hojas ocultas o muy ocultas llegan en la lista como cualquier otra), dimensiones del rango usado, conteos de celdas ni propiedades del documento; y aunque docProps/core.xml también es una parte pequeña, actualmente no existe un sondeo solo de propiedades, así que los metadatos de autor y título cuestan un Open completo. Estructuren la entrada en consecuencia: permitan que los hechos baratos enruten cada archivo, y recopilen los hechos costosos solo para los archivos que sobreviven al enrutamiento. Una nota de rendimiento para los archivos que sí continúan: un escaneo profundo de solo lectura de un .xls grande corre sensiblemente más rápido con _DisableGraphics := True, que omite el parseo de OfficeArt; pero nunca guarden desde esa instancia, porque la capa de dibujo que omitió ya no está.
Preguntas frecuentes
¿GetSheetNames carga datos de celdas?
No. Lee workbook.xml desde paquetes basados en zip o los registros BoundSheet desde archivos BIFF, así que su costo permanece plano sin importar cuántos datos contenga el libro.
¿Puedo listar las hojas de un archivo protegido con contraseña?
No mientras esté cifrado. Detecten el contenedor con CanReadEncrypted; los archivos .xls heredados pueden abrirse después con su contraseña, mientras que los .xlsx cifrados deben descifrarse en Excel antes de que HotXLS pueda leerlos.
¿Cómo sé si una hoja listada está oculta?
No pueden saberlo desde la llamada de listado: los indicadores de visibilidad requieren un Open completo. Traten la lista ligera como entrada para enrutamiento estructural, no como inventario completo.
Los archivos que pasan el triaje normalmente avanzan a análisis más profundo; la mesa de auditoría y conversión de libros muestra los contadores por hoja que conviene recopilar cuando una apertura completa ya se justifica, y la guía de rendimiento para libros grandes cubre cómo mantener rápida esa apertura completa.
HotXLS es una biblioteca nativa Object Pascal para hojas de cálculo en Delphi y C++Builder; la superficie completa de API, incluidas las llamadas de inspección que se muestran aquí, está documentada en la página de producto de HotXLS Component.