kostenloser Webspace werbefrei: lima-city


Excel-Datei in Response mit Java

lima-cityForumProgrammiersprachenJava

  1. Autor dieses Themas

    hk1992

    Moderator Kostenloser Webspace von hk1992

    hk1992 hat kostenlosen Webspace.

    Hallo,

    ich habe in Java ein HttpServlet, dass ich mit der POST-Methode aufrufe. Als Response soll eine auf dem Server erstellte Excel-Datei mitgegeben werden. Die Excel-Datei möchte ich mit http://poi.apache.org/ erstellen. Nun weiß ich nicht, wie ich das fertige Workbook in die Response bekomme, sodass es zum Client geschickt wird. Hier mein (gekürztes) Servlet:
    import java.text.SimpleDateFormat;
    import java.util.Calendar;
    
    import javax.servlet.ServletException;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    import org.apache.poi.ss.usermodel.Workbook;
    import org.apache.poi.xssf.usermodel.XSSFWorkbook;
    
    import com.google.inject.Singleton;
    
    @Singleton
    public class ExportServlet extends HttpServlet {
    
       private static final long serialVersionUID = 95239893244381473L;
    
       String fileName;
    
       public void doPost(HttpServletRequest req, HttpServletResponse resp)
    			throws ServletException, IOException {
    
          Calendar c1 = Calendar.getInstance();
          SimpleDateFormat sdf = new SimpleDateFormat("ddMMyyy");
          String dateStr = sdf.format(c1.getTime());
          fileName = "Compliance_Report_" + dateStr + ".xlsx";
    
          Workbook wb = new XSSFWorkbook();
    
          response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
          response.setHeader("Content-Disposition", "attachment; filename="
    				+ fileName);
      }
    }

    Das ganze kann als kleines Beispiel gesehen werden. Natürlich wird das Workbook an anderer Stelle gebaut, aber es geht mir nur darum, wie ich es mit in die Response bekomme, sodass es beim Client (Browser) zum Download angeboten wird.

    Im Guide steht für ein neues Workbook folgendes:
    Workbook wb = new XSSFWorkbook();
    FileOutputStream fileOut = new FileOutputStream("workbook.xlsx");
    wb.write(fileOut);
    fileOut.close();

    Danach stellt sich aber immer noch die Frage, wie ich den FileOutputStream in die Response bekomme.

    Danke + Grüße,
    Henning

    Beitrag zuletzt geändert: 12.2.2013 16:26:45 von hk1992
  2. Diskutiere mit und stelle Fragen: Jetzt kostenlos anmelden!

    lima-city: Gratis werbefreier Webspace für deine eigene Homepage

  3. Hallo hk1992,

    ich würde mal folgendes probieren:
    OutputStream sout = resp.getOutputStream();
    wb.write(sout);
    sout.flush();

  4. Hallo hk1992,

    oder deinen etwas modifizierten Code:
    ServletOutputStream servletOutputStreamObj = response.getOutputStream();
    Workbook wb = new XSSFWorkbook();
    
          response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
          response.setHeader("Content-Disposition", "attachment; filename="
    				+ fileName);
    wb.write(servletOutputStreamObj);

    Content-Length mitschicken wäre auch zu empfehlen.

    mfg,
    timebandit

    Beitrag zuletzt geändert: 12.2.2013 21:02:56 von timebandit
  5. Autor dieses Themas

    hk1992

    Moderator Kostenloser Webspace von hk1992

    hk1992 hat kostenlosen Webspace.

    Ich habe nun folgenden Code, der mir auch eine .xlsx Datei liefert. Jedoch sagt Excel, die Datei sei beschädigt, wenn ich versuche sie zu öffnen. Ich habe zum Test hier einfach eine Zelle mit dem Inhalt "Test" auf einem Datenblatt "Test" angelegt.
    public void doPost(HttpServletRequest req, HttpServletResponse resp)
    	throws ServletException, IOException {
    	Calendar c1 = Calendar.getInstance();
    	SimpleDateFormat sdf = new SimpleDateFormat("ddMMyyy");
    	String dateStr = sdf.format(c1.getTime());
    	fileName = "Compliance_Report_" + dateStr + ".xlsx";
    
    	Workbook wb = createExportFile(fileName);
    	ByteArrayOutputStream bos = new ByteArrayOutputStream();
    	wb.write(bos);
    
    	prepareResponseFor(resp, bos, fileName);
    
    	OutputStream sout = resp.getOutputStream();
    	wb.write(sout);
    	sout.flush();
    }
    
    private void prepareResponseFor(HttpServletResponse response,
    		ByteArrayOutputStream bos, String fileName) {
    
    	byte[] b = bos.toByteArray();
    	response.setContentLength((int) b.length);
    	response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
    	response.setHeader("Content-Disposition", "attachment; filename="
    			+ fileName);
    }
    
    private Workbook createExportFile(String fileName) {
    	Workbook wb = new XSSFWorkbook();
    
    	Sheet s1 = wb.createSheet(WorkbookUtil.createSafeSheetName("Test"));
    	Row r1 = s1.createRow((short) 1);
    	r1.createCell(1).setCellValue("Test");
    
    	return wb;
    }

    timebandit schrieb:
    Content-Length mitschicken wäre auch zu empfehlen.
    Habe ich auch mal versucht einzubauen. Stimmt das so, wie ich das mit dem byte-Array gemacht habe?

    Was ist der Unterschied zwischen den Lösungen bzw. dem ServletOutputStream und dem OutputStream?
  6. Hallo hk1992,

    in diesem Fall macht es keinen Unterschied, ob Du ServletOutputStream oder einfach nur OutputStream verwendest.
    Die Methode HttpServletResponse.getOutputStream() liefert ein Objekt vom Typ ServletOutputStream zurück. Dieses Objekt ist aber von OutputStream abgeleitet, weshalb es natürlich auch ein OutputStream ist.
    Da wir uns hier nur für die Methode flush() interessieren reicht es vollkommen sout als OutputStream zu deklarieren.
    ServletOutputStream bräuchte man nur wenn man die print() und println() Methoden verwenden wollte. Die sind in OutputStream nicht enthalten.
    Im übrigen hat ByteArrayOutputStream eine size() Methode mit der man die Größe erfragen kann. Desweiteren verfügt die Klasse über eine writeTo() Methode mit der sich der Inhalt in einen andere Stream kopieren lässt.
    D.h. man könnte mal folgendes versuchen:
    public void doPost(HttpServletRequest req, HttpServletResponse resp)
    	throws ServletException, IOException {
    	Calendar c1 = Calendar.getInstance();
    	SimpleDateFormat sdf = new SimpleDateFormat("ddMMyyy");
    	String dateStr = sdf.format(c1.getTime());
    	fileName = "Compliance_Report_" + dateStr + ".xlsx";
    
    	Workbook wb = createExportFile(fileName);
    	ByteArrayOutputStream bos = new ByteArrayOutputStream();
    	wb.write(bos);
    
    	resp.setContentLength(bos.size());
    	resp.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
    	resp.setHeader("Content-Disposition", "attachment; filename="+ fileName);
    
    	OutputStream sout = resp.getOutputStream();
    	bos.writeTo(sout);
    	sout.flush();
    	sout.close();
    }
    
    private Workbook createExportFile(String fileName) {
    	Workbook wb = new XSSFWorkbook();
    
    	Sheet s1 = wb.createSheet(WorkbookUtil.createSafeSheetName("Test"));
    	Row r1 = s1.createRow((short) 1);
    	r1.createCell(1).setCellValue("Test");
    
    	return wb;
    }
    Ich habe noch ein sout.close() hinzugefügt, in der Hoffnung, dass das das Problem mit der unvollständigen Datei löst.
  7. Autor dieses Themas

    hk1992

    Moderator Kostenloser Webspace von hk1992

    hk1992 hat kostenlosen Webspace.

    Danke für die Erklärung!
    Die Datei ist leider immer noch beschädigt, trotz sout.close(). Ich habe das ganze jetzt so verändert wie beschrieben.

    Weiß jemand, warum die Datei nicht geöffnet werden kann? Die Kommunikation zwischen Client und Server scheint ja jetzt soweit zu funktionieren.
    Vielleicht geht es über einen FileOutputStream, also mit wb.write(fos)? Aber dann müsste ich den FileOutputStream irgendwie in die Response bekommen, er bietet aber kein writeTo(). Wie bekomme ich den FileOutputStream in den OutputStream der Response?

    Beitrag zuletzt geändert: 13.2.2013 15:57:05 von hk1992
  8. Hallo hk1992,

    erst mal eine doofe Frage:
    Hast Du mit dem Apache POI schon mal erfolgreich eine Excel-Mappe erstellt, welche Du öffnen konntest?
    Ich hoffe jetzt einfach mal, dass die Antwort auf diese Frage 'Ja' ist. Ansonsten sollte man erst mal eine normale Java-Anwendung schreiben und sicherstellen, dass auch wirklich brauchbare Excel-Mappen erzeugt werden.
    Ansonsten ist jetzt systematisches Debugging angesagt.
    Als erstes sollte man sicherstellen, dass das was in den ByteArrayOutputStream geschrieben wird das selbe ist wie das was auch in die Datei geschreiben wird. D.h. ein Test-Workbook erstellen und erst direkt mittels FileOutputStream in eine Datei schreiben. Danach das selbe Workbook in einen ByteArrayOutputStream schreiben, den Rückgabewert der size()-Methode auf Übereinstimmung mit der Dateigröße überprüfen und dann den ByteArrayOutputStream mittels writeTo()-Methode in einen anderen FileOutputStream kopieren.
    Jetzt kann man beide Dateien noch mit einem Hex-Editor vergleichen (z.B. HxD).
    Wenn beide Dateien übereinstimmen, dann kann man zumindest sagen, dass das erzeugen der Excel-Mappe und das schreiben in den ByteArrayOutputStream nicht das Problem ist.
    Dann bliebe also das kopieren in den OutputStream des HTTPServletResponse bzw. das eigentliche Übertragen übrig.
    Dazu kann man erst mal versuchen, kleinere Datenmengen in den OutputStream zu schreiben. Also z.B. erst eine ByteArray der Größe 16, dann 32, 64 usw. bis 2048, und immer anschauen, was beim Client ankommt. Je nachdem was dabei rauskommt könnte man dann schauen, wo das Problem ist bzw. wie es sich evtl. umgehen lässt.
  9. Autor dieses Themas

    hk1992

    Moderator Kostenloser Webspace von hk1992

    hk1992 hat kostenlosen Webspace.

    darkpandemic schrieb:
    Hast Du mit dem Apache POI schon mal erfolgreich eine Excel-Mappe erstellt, welche Du öffnen konntest?

    Ja.

    Ich habe nun auch eine Lösung gefunden, komisch, dass ich da nicht früher drauf gekommen bin. Ich habe nach wb.write(bos) noch ein bos.flush() eingebaut und nachdem bos.writeTo(sout) noch ein bos.close(). Jetzt werden beide Streams geflusht und geschlossen. Problem erledigt. Meine Excel-Datei kommt an. Danke!
  10. Diskutiere mit und stelle Fragen: Jetzt kostenlos anmelden!

    lima-city: Gratis werbefreier Webspace für deine eigene Homepage

Dir gefällt dieses Thema?

Über lima-city

Login zum Webhosting ohne Werbung!