[JAVA] Notes personnelles lors de l'utilisation du classeur SXSSF (il est prudent d'éviter les unités d'accès ligne par ligne / vous ne pouvez pas accéder aux lignes qui ont déjà été écrites dans un fichier xlsx existant)

Aperçu

Lorsque vous utilisez le classeur XSSF avec Apache POI, c'est un problème de mémoire qui me soulève la tête. XSSF Workbook étend toutes les données lues et écrites en mémoire. Par conséquent, si vous ne faites pas attention lors de la création ou de la lecture d'Excel de grande taille, il est facile d'obtenir OutOfMemoryError.

Pour résoudre ce problème de mémoire gourmande du classeur XSSF, Apache POI fournit une API appelée SXSSFWorkbook qui économise la consommation de mémoire en écrivant toutes les données dans un fichier temporaire au lieu de l'étendre en mémoire. Puisqu'il implémente la même interface de classeur que le classeur XSSF,

Workbook book = new XSSFWorkbook();
// ↓
Workbook book = new SXSSFWorkbook();

Comme vous pouvez le voir, il arrive souvent que seule la classe d'implémentation soit remplacée pour économiser la consommation de mémoire, mais il y a quelques précautions à prendre lors de l'utilisation du classeur SXXF.

1. Il est plus sûr d'éviter les unités d'accès ligne par ligne

La logique d'économie de mémoire de SXSSFWorkbook, c'est-à-dire la logique d'écriture dans un fichier temporaire, "conserve uniquement la ligne windowSize dans la mémoire, et au moment où vous essayez de créer plus de lignes, toutes les lignes avant d'écrire dans le fichier temporaire. C'est. Et vous ne pouvez pas accéder à la ligne précédente qui a été écrite dans le fichier temporaire.

Cela peut être confirmé dans le code source ci-dessous.

try (Workbook book = new SXSSFWorkbook()) {
    Sheet sheet = book.createSheet();

    //Écrivez de la 2ème ligne à la 1000ème ligne.
    for (int i = 1; i < 1000; i++) {
        sheet.createRow(i).createCell(0).setCellValue(String.valueOf(i));
    }

    //J'ai oublié d'écrire dans la deuxième rangée, donc même si j'essaye de prendre la rangée dans la deuxième rangée
    //Impossible d'écrire car la valeur de retour sera nulle
    sheet.getRow(1); // => null

    //J'ai oublié d'écrire dans la première ligne, donc même si j'essaye de créer une ligne dans la première ligne
    //exception(*)Se produira et l'application sera arrêtée au lieu d'être incapable d'écrire.
    sheet.createRow(0);
} catch (IOException e) {
    e.printStackTrace();
}

Les exceptions qui se produisent dans ce qui précède (*) sont les suivantes.

Exception in thread "main" java.lang.IllegalArgumentException: Attempting to write a row[0] in the range [0,899] that is already written to disk.
	at org.apache.poi.xssf.streaming.SXSSFSheet.createRow(SXSSFSheet.java:131)
	at org.apache.poi.xssf.streaming.SXSSFSheet.createRow(SXSSFSheet.java:65)
	at poi.Main.main(Main.java:25)

Pour le moment, un accès aléatoire est possible pour les lignes conservées en mémoire. Le windowSize qui détermine les lignes à conserver en mémoire peut également être changé avec le constructeur et le setter, mais je pense que le contrôle a tendance à être compliqué et qu'il peut créer des bogues associés. Personnellement, lorsque vous utilisez le classeur SXSSF, je pense qu'il est préférable d'éviter l'accès aléatoire ligne par ligne et d'accéder séquentiellement de la ligne supérieure à la ligne inférieure.

2. Lignes inaccessibles écrites dans un fichier xlsx existant

Vous souhaiterez peut-être également écrire des données dans un fichier xlsx existant à l'aide du classeur SXSSF. À ce stade, veuillez noter que __ "Les lignes écrites dans le fichier xlsx existant sont écrites dans le fichier temporaire et ne sont pas accessibles par le classeur SXSSF". __

Par exemple, supposons que vous ayez un fichier Excel «2-1000.xlsx» qui a été écrit de la ligne 2 à la ligne 1000. Lisez ceci et essayez d'accéder à la ligne écrite.

try (Workbook book = new SXSSFWorkbook(new XSSFWorkbook("2-1000.xlsx"))) {
    Sheet sheet = book.getSheetAt(0);

    //J'ai oublié d'écrire dans la deuxième rangée, donc même si j'essaye de prendre la rangée dans la deuxième rangée
    //Impossible d'écrire car la valeur de retour sera nulle
    sheet.getRow(1); // => null

    //J'ai oublié d'écrire dans la première ligne, donc même si j'essaye de créer une ligne dans la première ligne
    //exception(*)Se produira et l'application sera arrêtée au lieu d'être incapable d'écrire.
    sheet.createRow(0);
} catch (IOException e) {
    e.printStackTrace();
}

Les exceptions qui se produisent dans ce qui précède (*) sont les suivantes.

Exception in thread "main" java.lang.IllegalArgumentException: Attempting to write a row[0] in the range [0,999] that is already written to disk.
	at org.apache.poi.xssf.streaming.SXSSFSheet.createRow(SXSSFSheet.java:138)
	at org.apache.poi.xssf.streaming.SXSSFSheet.createRow(SXSSFSheet.java:65)
	at poi.Main.main(Main.java:21)

Il y a souvent un cas d'utilisation où vous avez quelque chose comme un fichier modèle dans votre système, écrivez des données dans le fichier modèle dans un traitement par lots ou un traitement en ligne, et l'utilisateur utilise le fichier de résultat, mais dans un tel cas d'utilisation, il est conçu correctement. Sinon, le classeur SXSSF ne sera pas disponible.

Informations environnementales (extrait de pom.xml)

<dependencies>
  <!-- https://mvnrepository.com/artifact/org.apache.poi/poi -->
  <dependency>
    <groupId>org.apache.poi</groupId>
    <artifactId>poi</artifactId>
    <version>4.1.2</version>
  </dependency>

  <!-- https://mvnrepository.com/artifact/org.apache.poi/poi-ooxml -->
  <dependency>
    <groupId>org.apache.poi</groupId>
    <artifactId>poi-ooxml</artifactId>
    <version>4.1.2</version>
  </dependency>
</dependencies>

<properties>
  <maven.compiler.source>11</maven.compiler.source>
  <maven.compiler.target>11</maven.compiler.target>
</properties>

référence

Recommended Posts

Notes personnelles lors de l'utilisation du classeur SXSSF (il est prudent d'éviter les unités d'accès ligne par ligne / vous ne pouvez pas accéder aux lignes qui ont déjà été écrites dans un fichier xlsx existant)