Saltar al contenido principal

mzone-images-storage

Ver en Git


Imágenes de Zonas de Gestión — Almacenamiento y Migración

Documentación del almacenamiento de imágenes de zonas de gestión (management zones) para la migración.

Ubicación en el filesystem

Las imágenes se guardan usando @capacitor/filesystem con Directory.Data, dentro de un subdirectorio por usuario.

Ruta base por plataforma

PlataformaDirectory.Data
Androidfile:///data/user/0/com.auravant/files/
iOSfile:///var/mobile/Containers/Data/Application/{APP_UUID}/Library/NoCloud/

Estructura de directorios

{Directory.Data}/
└── {userId}/
├── mZone/ ← imágenes permanentes
│ ├── {imgUuid1} ← archivo sin extensión
│ ├── {imgUuid2}
│ └── ...
└── mzTemp/ ← imágenes temporales (pre-guardado)
├── localName0
├── localName1
└── ...

Ejemplo real de path completo

file:///data/user/0/com.auravant/files/123456/mZone/a1b2c3d4-e5f6-11ee-b7c8-0242ac120002

Formato de nombre de archivos

DirectorioNombre del archivoDescripción
mZone/UUID v1 (sin extensión)Imagen guardada permanentemente. El nombre es el uuid del objeto MZImg
mzTemp/localName{N} (N = 0, 1, 2, 3)Imagen temporal antes de confirmar la zona. Se borra al confirmar o cancelar
  • No tienen extensión de archivo. El mimetype se guarda por separado en el modelo de datos.
  • Formato del contenido: base64 escrito directamente con Filesystem.writeFile(). Al leer se obtiene base64 y se reconstruye con data:image/{mimetype};base64,{data}.
  • Máximo 4 imágenes por texto/nota (constante LIMIT = 4 en MZoneImageService).

Modelo de datos — Relación imagen ↔ zona

MZone (zona de gestión)
├── uuid: string ← ID de la zona
├── field_id: string ← ID del lote
├── texts: Array<MZText> ← notas/textos de la zona
│ └── MZText
│ ├── uuid: string ← ID del texto
│ ├── zone_id: string ← referencia al MZone.uuid
│ ├── text: string ← contenido del texto
│ └── images: Array<MZImg>
│ └── MZImg
│ ├── uuid: string ← ID de la imagen = NOMBRE DEL ARCHIVO en disco
│ ├── text_id: string ← referencia al MZText.uuid
│ ├── path: string ← URI completa del archivo en disco
│ ├── mimetype: string ← "jpeg" | "png" | "gif"
│ └── coordinates: string ← "POINT(lon lat)" o ""

Cómo se linkea la imagen con la zona

MZone.uuid → MZText.zone_id → MZImg.text_id

├── MZImg.uuid = nombre del archivo en mZone/
└── MZImg.path = URI completa (ej: file:///.../{userId}/mZone/{uuid})

Persistencia del modelo en SQLite

Las zonas de gestión (incluyendo las referencias a imágenes) se guardan en SQLite bajo la key "mz" en la tabla U{userId}:

-- Se lee igual que offline: por partes
SELECT data FROM "U{userId}" WHERE id = 'userkeyparts';
-- → {"mz": 2, "offline": 1, ...}

SELECT data FROM "U{userId}" WHERE id = 'mz1';
SELECT data FROM "U{userId}" WHERE id = 'mz2';
-- Concatenar y JSON.parse()

El JSON resultante tiene la estructura:

interface MZones {
[fieldId: string]: Array<MZone>;
}

Ejemplo:

{
"field-uuid-abc": [
{
"uuid": "zone-uuid-123",
"field_id": "field-uuid-abc",
"area": 50.5,
"code": "MZ-001",
"spot_name": "MZ-001",
"spot_shape": "POLYGON((-60.1 -33.2, ...))",
"tags": [{"uuid": "tag-1", "name": "Soja", "label": "Soja"}],
"vars": [{"name": "Rendimiento", "unitId": 1, "value": 3500}],
"timestamp_usr": "2024-01-15T10:30:00.00Z",
"texts": [
{
"uuid": "text-uuid-456",
"zone_id": "zone-uuid-123",
"text": "Zona con buen desarrollo",
"timestamp_usr": "2024-01-15T10:30:00.00Z",
"images": [
{
"uuid": "a1b2c3d4-e5f6-11ee-b7c8-0242ac120002",
"text_id": "text-uuid-456",
"path": "file:///data/user/0/com.auravant/files/123456/mZone/a1b2c3d4-e5f6-11ee-b7c8-0242ac120002",
"mimetype": "jpeg",
"coordinates": "POINT(-60.123 -33.456)"
}
]
}
]
}
]
}

Cola offline — PUTF de imágenes

Cuando se crea una zona con imágenes, se encola un PUTF por cada imagen en el sender mzone-img:

{
"id": 55,
"api": "/spots/msgs/imgs",
"body": "",
"method": "PUTF",
"opts": {
"params": {
"uuid": "a1b2c3d4-e5f6-11ee-b7c8-0242ac120002",
"mimetype": "jpeg",
"path": "file:///data/user/0/com.auravant/files/123456/mZone/a1b2c3d4-e5f6-11ee-b7c8-0242ac120002"
},
"headers": {
"Authorization": "Bearer eyJ..."
}
},
"eventData": {"msg": 104},
"attempts": 0,
"date": 1705312200000,
"userId": "123456"
}

Cómo se envía al servidor

  1. Se lee el archivo: Filesystem.readFile({path: "{userId}/mZone/{uuid}"})
  2. Se convierte a blob: fetch("data:image/{mimetype};base64,{data}").then(r => r.blob())
  3. Se envía como FormData: PUT /spots/msgs/imgs?uuid={imgUuid} con fd.append('img', blob)

Si el archivo no existe

Si al intentar enviar el archivo no se encuentra en disco (ej: el usuario borró datos), se emite el error 'path not found' y la request se elimina de la cola.

Pasos para migrar las imágenes

1. Leer el modelo de zonas desde SQLite

SELECT data FROM "U{userId}" WHERE id LIKE 'mz%' AND id != 'userkeyparts';

O mejor, leer userkeyparts → obtener cantidad de partes de mz → leer mz1, mz2, etc.

2. Parsear y recorrer las imágenes

const mZones: MZones = JSON.parse(concatenatedData);
for (const fieldId of Object.keys(mZones)) {
for (const zone of mZones[fieldId]) {
for (const text of zone.texts) {
for (const img of text.images) {
// img.uuid = nombre del archivo
// img.path = URI completa en disco
// img.mimetype = formato de la imagen
// Archivo en: {Directory.Data}/{userId}/mZone/{img.uuid}
}
}
}
}

3. Leer los archivos de imagen

Los archivos están en {Directory.Data}/{userId}/mZone/{imgUuid} como base64 (sin header, solo el contenido base64 raw).

4. Verificar imágenes pendientes en la cola offline

Si hay requests PUTF en requestQueue["mzone-img"], esas imágenes aún no se subieron al servidor. Se pueden identificar por:

  • method === "PUTF"
  • opts.params.uuid = UUID de la imagen
  • opts.params.path = path del archivo en disco

5. Imágenes que pueden no existir en disco

  • Si img.path está vacío ("") en el modelo, la imagen existe en el servidor pero nunca se descargó al dispositivo.
  • La descarga se hace bajo demanda via GET /spots/msgs/imgs/{imgUuid} (retorna blob).

Resumen de relaciones

DatoDónde estáCómo se accede
Lista de zonas con sus imágenesSQLite → U{userId} → key mzLeer partes, concatenar, JSON.parse
Archivo de imagenFilesystem → {userId}/mZone/{imgUuid}Leer con Capacitor Filesystem (base64)
Request pendiente de subidaSQLite → U{userId} → key offlinerequestQueue["mzone-img"]Dentro del OfflineData
Metadata de imagenDentro del JSON de mZonesMZImg.uuid, .mimetype, .coordinates
Relación imagen → zonaMZImg.text_idMZText.uuidMZText.zone_idMZone.uuidNavegación jerárquica