Notes pour lire et générer des fichiers xlsx à partir de Java à l'aide d'Apache POI

À propos de cet article

L'objectif est que les personnes souhaitant utiliser Apache POI puissent travailler avec des fichiers Excel lors de la copie. L'exemple d'implémentation présenté ici n'est que l'implémentation minimale pour exécuter Apache POI, et la gestion des erreurs est requise séparément. La version qui a été confirmée pour fonctionner avec le code est 4.1.0.

Préparation du POI Apache

Vous pouvez le préparer à partir d'ici. https://poi.apache.org/download.html

Objets dans Apache POI

ʻOrg.apache.poi.ss.usermodel.Workbook` représente le classeur entier du fichier xlsx.

Pour que le fichier xlsx soit lu ou écrit, spécifiez d'abord la feuille cible après avoir créé l'objet Workbook. C'est ʻorg.apache.poi.ss.usermodel.Sheet`.

Après avoir récupéré l'objet Sheet de l'objet Workbook, spécifiez la ligne à lire / écrire. C'est ʻorg.apache.poi.ss.usermodel.Row`.

Après avoir obtenu l'objet Row de l'objet Sheet, spécifiez la cellule à lire ou à écrire. Spécifiez la colonne et spécifiez la cellule. C'est ʻorg.apache.poi.ss.usermodel.Cell`.

Après avoir obtenu l'objet Cell de l'objet Row, vous pouvez lire et écrire des valeurs dans la cellule comme vous le feriez normalement dans Excel.

Lire le fichier .xlsx

Obtenir l'objet Cell

Tout d'abord, récupérez l'objet Workbook à partir du chemin du fichier.

Workbook workbook = WorkbookFactory.create(new FileInputStream(filePath));

L'objet Sheet peut être obtenu à partir du nom et de l'index de la feuille (à partir de 0 à partir de la feuille la plus à gauche).

//Obtenu à partir du nom de la feuille
Sheet sheet = workbook.getSheet(name);

//Obtenir de l'index
Sheet sheet = workbook.getSheetAt(index);

A partir de l'objet Sheet, récupérez l'objet Row puis l'objet Cell.

Row row = sheet.getRow(rowIndex);
Cell cell = row.getCell(columIndex);

Fondamentalement, pour obtenir l'objet Row et l'objet Cell, il est nécessaire de spécifier l'index de 0 start tel que la ligne et la colonne, mais en utilisant CellReference, c'est familier dans Excel. Il est également possible de spécifier au format A1.

CellReference reference = new CellReference(a1Position);
Row row = sheet.getRow(reference.getRow());
Cell cell = row.getCell(reference.getCol());

Obtenez le contenu de la cellule

Après avoir obtenu l'objet Cell, si vous voulez obtenir le contenu de la cellule sous forme de chaîne de caractères, exécutez la méthode Cell.getCellType () pour obtenir le type de valeur contenue dans la cellule, et combien en fonction de chaque Ou vous devez concevoir la méthode correspondante individuellement.

Si le contenu de la cellule est une chaîne (STRING), il peut être lu avec le standard Cell.getStringCellValue ().

Dans le cas d'une valeur numérique (NUMERIC), la date est également traitée comme une valeur numérique, il est donc préférable de faire attention à cela et de préparer votre propre méthode. Dans le cas des dates, dans la plupart des cas, vous voudrez une valeur qui a un format d'affichage stylisé, afin que vous puissiez gérer cela avec votre propre méthode.

Dans le cas de la valeur de vérité (BOOLEAN), vous pouvez obtenir la valeur de boolean avec le standardCell.getBooleanCellValue (), alors convertissez-le en chaîne de caractères.

Pour une valeur de fonction (FORMULA), si vous voulez simplement que l'expression de la fonction soit une chaîne, vous pouvez l'obtenir avec Cell.getCellFormula (). Cependant, en général, je pense que dans la plupart des cas, vous voulez le résultat du calcul de la fonction, alors dans ce cas, préparez votre propre méthode.

S'il n'y a pas de valeur (BLANK), il s'agit souvent d'une cellule fusionnée. La cellule fusionnée est censée avoir une valeur dans la cellule supérieure gauche de la zone fusionnée, donc la lecture d'une autre cellule dans la zone fusionnée sera traitée comme vide. Étant donné que les cellules fusionnées sont enregistrées de manière spéciale lors de l'utilisation d'Excel, nous préparerons une méthode distincte pour gérer cela.

Voici une implémentation simple d'une méthode qui prend un objet Cell et renvoie une String: Nous parlerons de nos propres méthodes ci-dessous.

String readCell(Cell cell) {
    switch (cell.getCellType()) {
    //Chaîne(la norme)
    case STRING:
    	return cell.getStringCellValue();
    //Valeur numérique(Posséder)
    case NUMERIC:
    	return getStringNmericValue(cell);
    //Valeur de vérité(la norme)
    case BOOLEAN:
    	return Boolean.toString(cell.getBooleanCellValue());
    //Valeur de la fonction(Posséder)
    case FORMULA:
    	return getStringFormulaValue(cell);
    //Vide(Posséder)
    case BLANK:
    	return getStringRangeValue(cell);
    default:
    	System.out.println("Unexpected Type: "+cell.getCellType());
    	return null;
    }
}

Propre méthode de valeur numérique (NUMERIC)

Si la valeur de la cellule n'est qu'un nombre, vous pouvez la convertir en chaîne et la renvoyer, mais s'il s'agit d'une date, adaptez le style, puis récupérez-la sous forme de chaîne.

Pour les formats de date standard, vous pouvez l'obtenir sous forme de chaîne en utilisant java.time, mais pour les types définis par l'utilisateur, utilisez ʻorg.apache.poi.ss.format.CellFormat` pour le styliser. Obtient la valeur de ce texte après l'adaptation.

Voici un exemple de mise en œuvre simple.

//Obtenez le numéro en tenant compte de la date
String getStringNmericValue(Cell cell) {
	//Pour les dates standard
	if(DateUtil.isCellDateFormatted(cell)) {
		Date date = cell.getDateCellValue();
		//java.time.Stringify avec DateTimeFormatter
		DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss.SSS");
		LocalDateTime localDateTime = LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault());
		return dateTimeFormatter.format(localDateTime);
	}
	//Si vous avez défini la date dans un type défini par l'utilisateur
	if(BuiltinFormats.FIRST_USER_DEFINED_FORMAT_INDEX <= cell.getCellStyle().getDataFormat()) {
		CellFormat cellFormat = CellFormat.getInstance(cell.getCellStyle().getDataFormatString());
		return cellFormat.apply(cell).text;
	}
	//S'il ne s'agit pas d'une date, elle renvoie un nombre.
	return Double.toString(cell.getNumericCellValue());
}

Propre méthode de valeur de la fonction (FORMULA)

Pour obtenir le résultat du calcul de la fonction, passez l'objet cible Cell à l'objet FormulaEvaluator et obtenez le résultat du calcul.

Cependant, comme le résultat du calcul est renvoyé sous la forme d'un objet CellValue au lieu d'une chaîne de caractères, le fait que le résultat du calcul soit une chaîne de caractères, une valeur numérique ou une valeur de vérité est à nouveau jugé par CellValue.getCellType (), et chacun est déterminé. , Lisez la valeur avec la méthode POI standard.

Notez également que certaines fonctions ne sont pas prises en charge par l'objet FormulaEvaluator. Veuillez consulter la liste des fonctions actuellement prises en charge à la fin. Si vous exécutez une fonction non prise en charge, ʻEvaluationException` sera lancée.

Si vous souhaitez exécuter une fonction définie par l'utilisateur, veuillez vous reporter ici. https://poi.apache.org/components/spreadsheet/user-defined-functions.html

Ce qui suit est un exemple d'implémentation simple de la propre méthode de la propre méthode de FORMULA.

//Exécuter la fonction et lire
String getStringFormulaValue(Cell cell) {
	try {
        //Exécution de la fonction
		CreationHelper helper = workbook.getCreationHelper();
		FormulaEvaluator evaluator = helper.createFormulaEvaluator();
		CellValue value = evaluator.evaluate(cell);

        //Modifiez la destination d'appel de la méthode en fonction du contenu du résultat du calcul.
		switch (value.getCellType()) {
		case STRING:
			return value.getStringValue();
		case NUMERIC:
			return Double.toString(value.getNumberValue());
		case BOOLEAN:
			return Boolean.toString(value.getBooleanValue());
		default:
			System.out.println("Unexpected Type: "+value.getCellType());
			return null;
		}
	}catch(Exception e) {
		return cell.getCellFormula();
	}
}

Méthode propre (BLANK) vierge

Dans le cas de BLANK, ce n'est pas un problème s'il ne peut pas être lu s'il s'agit simplement d'une cellule vide, mais dans le cas d'une cellule fusionnée, c'est un problème si elle ne peut pas être lue, alors faites-la correspondre à la cellule fusionnée.

Les cellules fusionnées sont stockées collectivement dans une zone spéciale de la feuille, et pour déterminer si la cellule à lire est une cellule fusionnée, retirez la liste de la zone de cellule fusionnée et lisez-la. Détermine s'il existe une zone fusionnée contenant les cellules dans.

S'il y a une zone fusionnée où la cellule à lire est incluse dans la zone, spécifiez le coin supérieur gauche de la zone pour lire la valeur.

Pour le moment, le type de contenu de la cellule ne peut pas être déterminé sans utiliser Cell.getCellType (), donc la cellule supérieure gauche est à nouveau transmise à la méthode qui renvoie String from Cell. (Méthode ReadCell dans l'exemple d'implémentation ci-dessus)

Voici un exemple de mise en œuvre simple.

//Lire les cellules fusionnées
String getStringRangeValue(Cell cell) {
    //Obtenez l'index de la cellule à lire
	int rowIndex = cell.getRowIndex();
	int columnIndex = cell.getColumnIndex();

    //Recherchez attentivement pour voir s'il est inclus dans la zone combinée.
	int size = sheet.getNumMergedRegions();
	for (int i = 0; i < size; i++) {
		CellRangeAddress range = sheet.getMergedRegion(i);
        //S'il y a une zone incluse
		if (range.isInRange(rowIndex, columnIndex)) {
			//Obtenez la cellule supérieure gauche
			Cell firstCell = sheet.getRow(range.getFirstRow()).getCell(range.getFirstColumn());
            //Cell->Jetez à la méthode String.
			return readCell(firstCell);
		}
	}
	return null;
}

Ecrire un fichier .xlsx

Création d'objets Workbook, Sheet, Row, Cell

Lors de la création d'un nouveau classeur au format xlsx, créez une instance de ʻorg.apache.poi.xssf.usermodel.XSSFWorkbooket recevez-la comme variable de typeWorkbook`.

Si vous voulez créer un objet Sheet, il est préférable de le convertir en un nom qui peut être utilisé pour une feuille avec` WorkbookUtil.createSafeSheetName () ʻet ensuite le créer.

En ce qui concerne l'objet Row et l'objet Cell, dans l'objet Sheet à l'état initial, toutes les lignes et cellules sont nulles, donc créez chaque objet une fois avant d'écrire dans la cellule. est nécessaire. Si vous souhaitez spécifier la ligne / colonne au format A1, utilisez CellReference comme vous l'avez fait dans la lecture ci-dessus.

//Création d'un objet Workbook
Workbook workbook = new XSSFWorkbook();

//Générer un objet Feuille
String safeName = WorkbookUtil.createSafeSheetName(name);
Sheet sheet = workbook.createSheet(safeName);

//Conversion du format A1 en index
CellReference reference = new CellReference(a1Position);
int rowIndex = reference.getRow();
int columIndex = reference.getCol();

//Générer un objet Row
Row row = sheet.createRow(rowIndex);

//Créer un objet Cell
Cell cell = row.createCell(columIndex);

Sortie de fichier

Après avoir apporté les différentes modifications ci-dessous à l'objet Workbook, utilisez FileOutputStream pour exporter finalement le fichier xlsx. (Bien que omis par souci de concision, veuillez écrire try-catch de manière appropriée dans le code réel.)

FileOutputStream fileOut = new FileOutputStream(filePath);
workbook.write(fileOut);
fileOut.close();

Ecrire une chaîne dans une cellule

Ecrire une chaîne est très simple, il suffit d'appeler Cell.setvalue (). Cependant, lors de la création d'une méthode qui écrit des cellules en spécifiant l'index, il est préférable de vérifier la valeur nulle de l'objet Row ou de l'objet Cell à cette position.

Voici un exemple de mise en œuvre simple.

void writeCellAt(int rowIndex, int columIndex, String value) {
    //Row,Vérification nulle de cellule
	Row row = sheet.getRow(rowIndex);
    if(row == null) {
        row = sheet.createRow(rowIndex);
    }
	Cell cell = row.getCell(columIndex);
    if(cell == null) {
		cell = row.createCell(columIndex);
	}

    //Ecrire une chaîne
	cell.setCellValue(value);
}

En gros, tant que vous pouvez écrire une chaîne de caractères, je pense qu'un traitement compliqué peut être implémenté à l'aide de fonctions et de bibliothèques Java.

S'il s'agit d'un simple format tabulaire, c'est possible avec un fichier csv, mais je pense qu'il y a des décorations de cellules comme raison d'utiliser xlsx. Je pense qu'il y a beaucoup de gens dans l'environnement où xlsx est utilisé comme premier document plutôt que PDF et dox, donc la méthode de décoration de base est indiquée ci-dessous. Il est toujours préférable de vérifier la valeur nulle de Row et Cell pour les décorations ci-dessous.

Fusionner les cellules

Cette fois, utilisez Sheet.addMergedRegion () pour vous enregistrer avec l'objet Sheet, comme il a été obtenu à partir de l'objet Sheet lors de la lecture des cellules fusionnées.

//topRow: Index de ligne de la cellule supérieure gauche
//topColum: Index de colonne de la cellule supérieure gauche
//bottomRow: Index de ligne de la cellule inférieure droite
//bottomColum: Index de colonne de la cellule inférieure droite
CellRangeAddress cellRangeAddress = new CellRangeAddress(topRow, bottomRow, topColum, bottomColum);
sheet.addMergedRegion(cellRangeAddress);

Changer la hauteur de ligne

Vous pouvez modifier la hauteur de ligne avec Row.setHeightInPoints (). S'il est difficile de spécifier une hauteur spécifique, vous pouvez la spécifier en multiples de la valeur par défaut.

//plusieurs
float mult = 2.0f;
//Définir deux fois la valeur par défaut
row.setHeightInPoints(mult*sheet.getDefaultRowHeightInPoints());

Pour envelopper et afficher le tout, utilisez CellStyle pour l'objet Cell cible comme suit:

CellStyle style = workbook.createCellStyle();
style.setWrapText(true);
cell.setCellStyle(style);

Si cela ne vous donne pas un bon ajustement dynamique, veuillez vous référer ici. https://stackoverflow.com/questions/19145628/auto-size-height-for-rows-in-apache-poi

De plus, si le réglage de la hauteur ne fonctionne pas, il semble y avoir un moyen de compter les sauts de ligne et de le définir comme un multiple. https://stackoverflow.com/questions/45387206/how-to-increase-the-height-of-the-excel-row-using-apache-poi-having-merged-cell/45388037

Dessin de bordure

Pour dessiner une bordure, utilisez CellStyle pour spécifier l'objet Cell cible individuellement, en haut, en bas, à gauche et à droite, comme indiqué ci-dessous. L'épaisseur est spécifiée par BorderStyle. C'est un nom déroutant, mais «BorderStyle.THIN» représente généralement une bordure épaisse et «BorderStyle.MEDIUM» représente une bordure épaisse.

CellStyle style = workbook.createCellStyle();
style.setBorderTop(BorderStyle.THIN);
style.setBorderBottom(BorderStyle.THIN);
style.setBorderLeft(BorderStyle.THIN);
style.setBorderRight(BorderStyle.THIN);

cell.setCellStyle(style);

Changer la couleur de la police

La couleur de la police peut également être modifiée avec CellStyle pour l'objetCell cible.

Pour les couleurs, une palette de couleurs standard est préparée à l'avance en tant que variable. Veuillez vous référer ici pour des échantillons de couleurs. https://lincolnminto.wordpress.com/2014/04/07/apache-poi-referencia-para-estilos-de-celulas-apache-poi-cellstyles-reference/

CellStyle style = workbook.createCellStyle();
Font font = workbook.createFont();
font.setColor(IndexedColors.WHITE.getIndex());
style.setFont(font);
cell.setCellStyle(style);

Remplissage de cellule

Le remplissage de la cellule peut également être modifié avec CellStyle pour l'objet Cell.

Encore une fois, de manière déroutante, utilisez «CellStyle.setFillForegroundColor ()» au lieu de «CellStyle.setFillBackgroundColor ()».

CellStyle style = workbook.createCellStyle();
style.setFillForegroundColor(IndexedColors.CORAL.getIndex());
style.setFillPattern(FillPatternType.SOLID_FOREGROUND);
cell.setCellStyle(style);

Je n'ai pas confirmé que cela fonctionne correctement, mais il semble y avoir un moyen de spécifier RVB pour la couleur de police et la couleur de remplissage de cellule comme suit.

IndexedColorMap colorMap = workbook.getStylesSource().getIndexedColors();
XSSFColor grey = new XSSFColor(new java.awt.Color(192,192,192), colorMap);
cellStyle.setFillForegroundColor(grey);

Référence: https://stackoverflow.com/questions/10912578/apache-poi-xssfcolor-from-hex-code

Coller l'image

Collez l'image sur l'objet Workbook.

Après la conversion en une chaîne d'octets avec ʻIOUtils.toByteArray () , en l'enregistrant dans le livre avec Workbook.addPicture () `, l'index de l'image est acquis comme valeur de retour. Certains des formats de fichier qui peuvent être spécifiés à ce moment sont indiqués ci-dessous.

constant format
PICTURE_TYPE_DIB bmp
PICTURE_TYPE_JPEG jpeg
PICTURE_TYPE_PNG png

La position et la taille de l'image sont spécifiées à l'aide de ClientAnchor. Spécifiez la cellule supérieure gauche et la cellule inférieure droite pour ajuster la position et la taille. Cependant, «ClientAnchor.setCol2 ()» et «ClientAnchor.setRow2 ()» qui spécifient la cellule inférieure droite sont en fait étirés car ils sont étirés jusqu'à la position d'index-1 spécifiée (une cellule supérieure gauche). Spécifiez la valeur de l'indice + 1 dans la cellule inférieure droite de la plage souhaitée.

Le type d'ancrage est ce qu'Excel appelle le champ de mise en forme de propriété d'une forme, et vous pouvez spécifier s'il faut apporter des modifications à l'image lorsque les cellules se déplacent ou se redimensionnent. Voici une liste de constantes et de paramètres.

constant La description
DONT_MOVE_AND_RESIZE Même si la ligne / colonne est redimensionnée, l'image n'est pas déplacée / redimensionnée.
DONT_MOVE_DO_RESIZE L'image n'est pas déplacée, mais elle est redimensionnée pour s'adapter à la cellule d'ancrage.
MOVE_AND_RESIZE Déplacer et redimensionner.
MOVE_DONT_RESIZE Il se déplace selon la cellule de l'ancre

Enfin, utilisez l'objet Drawing pour coller l'image avec l'index spécifié à la position d'ancrage spécifiée. Voici un exemple de mise en œuvre simple. (Bien que omis par souci de concision, veuillez écrire try-catch de manière appropriée dans le code réel.)

//Convertir en chaîne d'octets
InputStream in = new FileInputStream(filePath);
byte[] bytes = IOUtils.toByteArray(in);
in.close();

//Enregistrement d'image, acquisition d'index
int pictureIndex = workbook.addPicture(byteWorkbook.PICTURE_TYPE_PNG);

//Spécifiez la position et la taille avec l'ancre
ClientAnchor anchor = workbook.getCreationHelper().createClientAnchor();
anchor.setCol1(topColum);
anchor.setRow1(topRow);
anchor.setCol2(bottomColum+1);
anchor.setRow2(bottomRow+1);
anchor.setAnchorType(AnchorType.MOVE_AND_RESIZE);

//Coller l'image de l'index spécifié à la position de l'ancre spécifiée
Drawing patriarch = sheet.createDrawingPatriarch();
patriarch.createPicture(anchor, pictureIndex);

[Annexe] Fonctions intégrées prises en charge par POI

Référence: https://poi.apache.org/components/spreadsheet/eval-devguide.html

ABS ACOS ACOSH ADDRESS AND
ASIN ASINH ATAN ATAN2 ATANH
AVEDEV AVERAGE BIN2DEC CEILING CHAR
CHOOSE CLEAN CODE COLUMN COLUMNS
COMBIN COMPLEX CONCATENATE COS COSH
COUNT COUNTA COUNTBLANK COUNTIF COUNTIFS
DATE DAY DAYS360 DEC2BIN DEC2HEX
DEGREES DELTA DEVSQ DGET DMIN
DOLLAR EDATE EOMONTH ERROR.TYPE EVEN
EXACT EXP FACT FACTDOUBLE FALSE
FIND FIXED FLOOR FV HEX2DEC
HLOOKUP HOUR HYPERLINK IF IFERROR
IMAGINARY IMREAL INDEX INDIRECT INT
INTERCEPT IPMT IRR ISBLANK ISERR
ISERROR ISEVEN ISLOGICAL ISNA ISNONTEXT
ISNUMBER ISODD ISREF ISTEXT LARGE
LEFT LEN LN LOG LOG10
LOOKUP LOWER MATCH MAX MAXA
MDETERM MEDIAN MID MIN MINA
MINUTE MINVERSE MIRR MMULT MOD
MODE MONTH MROUND NA NETWORKDAYS
NOT NOW NPER NPV OCT2DEC
ODD OFFSET OR PERCENTILE PI
PMT POISSON POWER PPMT PRODUCT
PROPER PV QUOTIENT RADIANS RAND
RANDBETWEEN RANK RATE REPLACE REPT
RIGHT ROMAN ROUND ROUNDDOWN ROUNDUP
ROW ROWS SEARCH SECOND SIGN
SIN SINH SLOPE SMALL SQRT
STDEV SUBSTITUTE SUBTOTAL SUM SUMIF
SUMIFS SUMPRODUCT SUMSQ SUMX2MY2 SUMX2PY2
SUMXMY2 T TAN TANH TEXT
TIME TODAY TRANSPOSE TRIM TRUE
TRUNC UPPER VALUE VAR VARP
VLOOKUP WEEKDAY WEEKNUM WORKDAY YEAR
YEARFRAC

Sites / articles référencés

--Qiita Apache POI mémorandum Excel lecture https://qiita.com/panage/items/4476ff91d1fcec67f525

Recommended Posts

Notes pour lire et générer des fichiers xlsx à partir de Java à l'aide d'Apache POI
[Java] Extraction de texte de PowerPoint (ppt) à l'aide d'Apache POI
[Java] Gérer les fichiers Excel avec Apache POI
[Java] Lecture et écriture de fichiers avec OpenCSV
Lire et écrire des fichiers gzip en Java
Consulter le mémo de la copie de fichier de Java 1.7 ou version ultérieure
[Review] Lecture et écriture de fichiers avec java (JDK6)
Fichiers de propriétés commutés pour le développement et la production à l'aide de JMockit
Affichage d'erreur JSP à partir du servlet
Un exemple CRUD simple utilisant Java Servlet / JSP et MySQL
Classes et instances Java pour les débutants
Notes pour lire et générer des fichiers xlsx à partir de Java à l'aide d'Apache POI
[Pour moi-même] Transfert du traitement de classe de servlet
Écrire du code à l'aide de classes et d'instances Ruby
Convertissez de gros fichiers XLSX en CSV avec Apache POI
Fichiers de propriétés commutés pour le développement et la production à l'aide de JMockit
[Java] Développement avec plusieurs fichiers en utilisant package et import
Comment écrire et noter lors de la migration de VB vers JAVA
Conseils d'utilisation de Salesforce SOAP et de l'API Bulk en Java
Opération Excel avec Apache POI
Essayez de gratter en utilisant Java [Note]
Apache Hadoop et Java 9 (partie 1)
apache POI mémo personnel crossfish21
Utilisation de Docker depuis Java Gradle
Instructions Java while et for
Sortie vers Excel en utilisant Apache POI!
Remarques sur la création de l'environnement de développement de Kotlin et la migration de Java vers Kotlin