web-dev-qa-db-fra.com

Comment créer et écrire dans un fichier Excel (.xlsx)?

Il existe des bibliothèques pour les développeurs Java qui disposent de nombreuses méthodes qui ne fonctionnent pas sous Android. 

J'ai commencé par travailler avec des bibliothèques comme OpenCSV, mais malheureusement, Excel rencontre des problèmes connus lors de l'ouverture de fichiers CSV.

Puis j'ai essayé d'utiliser:

  • Apache POI - Il a définitivement trop de méthodes. 
  • JExcelAPI - Cela fonctionne, mais uniquement sur les anciens fichiers binaires .xls.
  • docx4j - encore une fois trop de bocaux, car il est basé sur JAXB qui n'est pas inclus dans Android.

Ma question est, comment puis-je créer un fichier Excel simple au format .xlsx sur Android, sans dépasser 65k méthodes?

16
Nominalista

Première réponse: faites-le côté serveur.

Si ce n'est pas possible, utilisez simplement JExecelAPI - pratiquement tout ce qui lit les fichiers xlsx lit également les fichiers xls.

Toutes les autres bibliothèques Excel vont être beaucoup trop grandes.

Une autre pensée - écrivez des fichiers csv manuellement ou avec l’une des nombreuses bibliothèques csv disponibles. Encore une fois, la plupart des applications qui lisent des fichiers Excel lisent également des fichiers csv.

14
GreyBeardedGeek

Puisque cette question semble plutôt être: "Quel est le moyen le plus léger de créer des fichiers Office Open XML pour Excel (*.xlsx)?", Je vais vous donner un exemple qui ne nécessite aucune bibliothèque, à l'exception des bibliothèques par défaut Java.lang, Java.io et Java.util.Zip.

Un fichier *.xlsx n'est rien d'autre qu'une archive Zip contenant des fichiers XML et d'autres fichiers dans une structure de répertoires. Il suffit donc d’une possibilité de créer, de lire et d’écrire des archives Zip et de créer, de lire et d’écrire des fichiers XML. J'utilise Java.util.Zip pour la partie Zip et la manipulation de chaîne pour la partie XML. Ceci, la création et la manipulation de XML via une manipulation de chaîne, n'est pas le moyen le plus recommandé pour manipuler XML, mais c'est le moyen le plus léger puisqu'il ne nécessite aucune bibliothèque XML supplémentaire.

Exemple complet:

import Java.io.OutputStream;
import Java.io.ByteArrayOutputStream;

import Java.util.Zip.*;

public class CreateXLSXFromScratch {

 //some static parts of the XLSX file:

 static String content_types_xml = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?><Types xmlns=\"http://schemas.openxmlformats.org/package/2006/content-types\"><Default ContentType=\"application/vnd.openxmlformats-package.relationships+xml\" Extension=\"rels\"/><Default ContentType=\"application/xml\" Extension=\"xml\"/><Override ContentType=\"application/vnd.openxmlformats-officedocument.extended-properties+xml\" PartName=\"/docProps/app.xml\"/><Override ContentType=\"application/vnd.openxmlformats-package.core-properties+xml\" PartName=\"/docProps/core.xml\"/><Override ContentType=\"application/vnd.openxmlformats-officedocument.spreadsheetml.sharedStrings+xml\" PartName=\"/xl/sharedStrings.xml\"/><Override ContentType=\"application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml\" PartName=\"/xl/styles.xml\"/><Override ContentType=\"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml\" PartName=\"/xl/workbook.xml\"/><Override ContentType=\"application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml\" PartName=\"/xl/worksheets/sheet1.xml\"/></Types>";

 static String docProps_app_xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><Properties xmlns=\"http://schemas.openxmlformats.org/officeDocument/2006/extended-properties\"><Application>" + "Created Low level From Scratch" + "</Application></Properties>";

 static String docProps_core_xml = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?><cp:coreProperties xmlns:cp=\"http://schemas.openxmlformats.org/package/2006/metadata/core-properties\" xmlns:dc=\"http://purl.org/dc/elements/1.1/\" xmlns:dcterms=\"http://purl.org/dc/terms/\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"><dcterms:created xsi:type=\"dcterms:W3CDTF\">" + Java.time.Instant.now().truncatedTo(Java.time.temporal.ChronoUnit.SECONDS).toString() + "</dcterms:created><dc:creator>" + "Axel Richter from scratch" + "</dc:creator></cp:coreProperties>";

 static String _rels_rels_xml  = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?><Relationships xmlns=\"http://schemas.openxmlformats.org/package/2006/relationships\"><Relationship Id=\"rId1\" Target=\"xl/workbook.xml\" Type=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument\"/><Relationship Id=\"rId2\" Target=\"docProps/app.xml\" Type=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships/extended-properties\"/><Relationship Id=\"rId3\" Target=\"docProps/core.xml\" Type=\"http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties\"/></Relationships>";

 static String xl_rels_workbook_xml_rels_xml = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?><Relationships xmlns=\"http://schemas.openxmlformats.org/package/2006/relationships\"><Relationship Id=\"rId1\" Target=\"sharedStrings.xml\" Type=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships/sharedStrings\"/><Relationship Id=\"rId2\" Target=\"styles.xml\" Type=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles\"/><Relationship Id=\"rId3\" Target=\"worksheets/sheet1.xml\" Type=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet\"/></Relationships>";

 static String xl_sharedstrings_xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><sst count=\"0\" uniqueCount=\"0\" xmlns=\"http://schemas.openxmlformats.org/spreadsheetml/2006/main\"/>"; 

 static String xl_styles_xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><styleSheet xmlns=\"http://schemas.openxmlformats.org/spreadsheetml/2006/main\"><numFmts count=\"0\"/><fonts count=\"1\"><font><sz val=\"11.0\"/><color indexed=\"8\"/><name val=\"Calibri\"/><family val=\"2\"/><scheme val=\"minor\"/></font></fonts><fills count=\"2\"><fill><patternFill patternType=\"none\"/></fill><fill><patternFill patternType=\"darkGray\"/></fill></fills><borders count=\"1\"><border><left/><right/><top/><bottom/><diagonal/></border></borders><cellStyleXfs count=\"1\"><xf numFmtId=\"0\" fontId=\"0\" fillId=\"0\" borderId=\"0\"/></cellStyleXfs><cellXfs count=\"1\"><xf numFmtId=\"0\" fontId=\"0\" fillId=\"0\" borderId=\"0\" xfId=\"0\"/></cellXfs></styleSheet>";

 static String xl_workbook_xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><workbook xmlns=\"http://schemas.openxmlformats.org/spreadsheetml/2006/main\" xmlns:r=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships\"><workbookPr date1904=\"false\"/><bookViews><workbookView activeTab=\"0\"/></bookViews><sheets><sheet name=\"" + "Sheet1" + "\" r:id=\"rId3\" sheetId=\"1\"/></sheets></workbook>";

 static String xl_worksheets_sheet1_xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><worksheet xmlns=\"http://schemas.openxmlformats.org/spreadsheetml/2006/main\"><dimension ref=\"A1\"/><sheetViews><sheetView workbookViewId=\"0\" tabSelected=\"true\"/></sheetViews><sheetFormatPr defaultRowHeight=\"15.0\"/><sheetData/><pageMargins bottom=\"0.75\" footer=\"0.3\" header=\"0.3\" left=\"0.7\" right=\"0.7\" top=\"0.75\"/></worksheet>";

 public static void main(String[] args) throws Exception {

  // result goes into a ByteArrayOutputStream
  ByteArrayOutputStream resultout = new ByteArrayOutputStream();

  // needed objects
  ZipEntry zipentry = null;
  byte[] data = null;

  // create ZipOutputStream
  ZipOutputStream zipout = new ZipOutputStream(resultout);

  // create the static parts of the XLSX Zip file:

  zipentry = new ZipEntry("[Content_Types].xml");
  zipout.putNextEntry(zipentry);
  data = content_types_xml.getBytes();
  zipout.write(data, 0, data.length);
  zipout.closeEntry();

  zipentry = new ZipEntry("docProps/app.xml");
  zipout.putNextEntry(zipentry);
  data = docProps_app_xml.getBytes();
  zipout.write(data, 0, data.length);
  zipout.closeEntry();

  zipentry = new ZipEntry("docProps/core.xml");
  zipout.putNextEntry(zipentry);
  data = docProps_core_xml.getBytes();
  zipout.write(data, 0, data.length);
  zipout.closeEntry();

  zipentry = new ZipEntry("_rels/.rels");
  zipout.putNextEntry(zipentry);
  data = _rels_rels_xml.getBytes();
  zipout.write(data, 0, data.length);
  zipout.closeEntry();

  zipentry = new ZipEntry("xl/_rels/workbook.xml.rels");
  zipout.putNextEntry(zipentry);
  data = xl_rels_workbook_xml_rels_xml.getBytes();
  zipout.write(data, 0, data.length);
  zipout.closeEntry();

  zipentry = new ZipEntry("xl/sharedStrings.xml");
  zipout.putNextEntry(zipentry);
  data = xl_sharedstrings_xml.getBytes();
  zipout.write(data, 0, data.length);
  zipout.closeEntry();

  zipentry = new ZipEntry("xl/styles.xml");
  zipout.putNextEntry(zipentry);
  data = xl_styles_xml.getBytes();
  zipout.write(data, 0, data.length);
  zipout.closeEntry();

  zipentry = new ZipEntry("xl/workbook.xml");
  zipout.putNextEntry(zipentry);
  data = xl_workbook_xml.getBytes();
  zipout.write(data, 0, data.length);
  zipout.closeEntry();

  // preparing the sheet data:

  Object[][] sheetData = new Object[][] {
   {"Text", "Value", "Formula"},
   {"Text1", 1.23456, "=SIN(B2)"},
   {"Text2", 2.34567, "=SQRT(B3)"},
   {"Text3", 123.456, "=B4/10"}
  };
  String sheetdata = "<sheetData>";
  int r = 0;
  char c = 'A'; --c;
  for (Object[] rowData : sheetData) {
   sheetdata += "<row r=\"" + ++r + "\">";
   c = 'A'; --c;
   for (Object cellData : rowData) {
    sheetdata += "<c r=\"" + Character.toString(++c) + r + "\"";
    if (cellData instanceof String && ((String)cellData).startsWith("=")) {
     sheetdata += "><f>" + ((String)cellData).replace("=", "") + "</f></c>";
    } else if (cellData instanceof String) {
     sheetdata += " t=\"inlineStr\"><is><t>" + ((String)cellData) + "</t></is></c>";
    } else if (cellData instanceof Double) {
     sheetdata += "><v>" + ((Double)cellData) + "</v></c>";
    }
   }
   sheetdata += "</row>";
  }
  sheetdata += "</sheetData>";

  // get the static sheet xml into a buffer for further processing
  StringBuffer xl_worksheets_sheet1_xml_buffer = new StringBuffer(xl_worksheets_sheet1_xml);

  // get position of the <dimension ref=\"A1\"/> in the static xl_worksheets_sheet1_xml
  int dimensionstart = xl_worksheets_sheet1_xml_buffer.indexOf("<dimension ref=\"A1\"/>");
  // replace the <dimension ref=\"A1\"/> with the new dimension
  xl_worksheets_sheet1_xml_buffer = xl_worksheets_sheet1_xml_buffer.replace(
   dimensionstart, 
   dimensionstart + "<dimension ref=\"A1\"/>".length(), 
   "<dimension ref=\"A1:" + Character.toString(c) + r + "\"/>");

  // get position of the <sheetData/> in the static xl_worksheets_sheet1_xml
  int sheetdatastart = xl_worksheets_sheet1_xml_buffer.indexOf("<sheetData/>");
  // replace the <sheetData/> with the prepared sheet date string
  xl_worksheets_sheet1_xml_buffer = xl_worksheets_sheet1_xml_buffer.replace(
   sheetdatastart, 
   sheetdatastart + "<sheetData/>".length(), 
   sheetdata);

  // create the xl/worksheets/sheet1.xml
  zipentry = new ZipEntry("xl/worksheets/sheet1.xml");
  zipout.putNextEntry(zipentry);
  data = xl_worksheets_sheet1_xml_buffer.toString().getBytes();
  zipout.write(data, 0, data.length);
  zipout.closeEntry();

  zipout.finish();

  // now ByteArrayOutputStream resultout contains the XLSX file data

  // writing this data into a file
  try (Java.io.FileOutputStream fileout = new Java.io.FileOutputStream("test.xlsx")) {
   resultout.writeTo(fileout);
   resultout.close();
  }

 }
}
8
Axel Richter

Nous sommes en 2018. Utilisez l'API Microsoft Graph et créez le fichier Excel dans O365.

Microsoft a publié quelques exemples dans Angular et C #. Ce n'est pas Java, mais c'est un bon point de départ: https://developer.Microsoft.com/en-us/graph/docs/concepts/Excel-write-tworkbook .

Le SDK Java MS Graph est compatible avec Android.

Limitation - il n'y a pas de moyen facile de créer un fichier Excel à partir de rien en utilisant l'API. Vous voudrez peut-être conserver un classeur vide et le cloner à chaque fois.

3
Michal

Utilisez Apache POI et activez le multi dex en ajoutant simplement compile "com.Android.support:multidex:1.0.1" à vos dépendances dans build.gradle. Vous devez également définir multiDexEnabled sur true. Cela devrait éliminer la limitation de la méthode 65k. 

0
Niza Siwale

Si vous n’avez pas besoin de lire le fichier xlsx depuis n’importe quelle source, je vous recommande d’utiliser CSV. Excel est parfaitement capable de convertir ce type en n’importe laquelle de ses sorties std. 

Même si vous avez besoin d'une formule, d'un format de cellule, il y a moyen de l'utiliser avec la librairie que vous citez ou en utilisant votre propre analyseur XML, comme dit beaucoup de gens avant moi, le format Excel n'est qu'un fichier Zip avec xml. Ces xml sont décrits ici:

https://msdn.Microsoft.com/en-us/library/dd979921(v=office.12).aspx

Comme vous le découvrirez, Excel est un format très complexe et la simple rétrocompatibilité n’est pas leur priorité. Donc, faire votre propre analyseur restreint avec XPath ou JAXB sera un travail difficile, mais pas impossible.

Mais je ne comprends pas pourquoi vous voulez limiter le nombre de méthodes? s'il s'agit de créer un logiciel intégré, à mon avis, vous ne devriez pas utiliser .xlsx, c'est un fichier trop compliqué et lourd pour sauvegarder des grilles ...