Why isn’t chrome downloading the files included in the response?

I am currently running a Java Spring Boot program that should return a response that includes two Excel files (.xlsx) as ByteArrayOutputStream’s in the ServletOutputStream as Content-Disposition: attachment;

However, upon returning that response to chrome, it isn’t downloading anything.

The Excel files are generated using the Apache POI and then they’re being written to a ByteArrayOutputStream.

Here’s my code on the file generation

protected ByteArrayOutputStream export() throws IOException{
    XSSFWorkbook workbook = new SXXFWorkbook();
    //some stuff happens
    
    ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
    workbook.write(byteArrayOutputStream);
    workbook.close();
    return byteArrayOutputStream;
}

Here is my endpoint that processes the request and returns the response.

@GetMapping("/${pageLinks.excelPage}/download")
public void getExcelDownload(HttpServletResponse response) throws IOException {
    // Set the response type and specify the boundary string
    response.setContentType("multipart/x-mixed-replace;boundary=END");

    // Set the content type based on the file type you need to download
    String contentType = "Content-type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";

    ByteArrayOutputStream inventorySheet = inventoryExcel.export();
    ByteArrayOutputStream deductionsSheet = deductionsExcel.export();

    ServletOutputStream responseOutputStream = response.getOutputStream();

    // Print the boundary string
    responseOutputStream.println();
    responseOutputStream.println("--END");

    List<Pair<String, ByteArrayOutputStream>> files = new ArrayList<>();

    files.add(new Pair<>("Deductions.xlsx", deductionsSheet));
    files.add(new Pair<>("Inventory.xlsx", inventorySheet));

    for (Pair<String, ByteArrayOutputStream> file : files) {

        // Print the content type
        responseOutputStream.println(contentType);
        responseOutputStream.println("Content-Disposition: attachment; filename=" + file.getKey());
        responseOutputStream.println();

        // Write the contents of the file
        file.getValue().writeTo(responseOutputStream);
        file.getValue().close();

        // Print the boundary string
        responseOutputStream.println();
        responseOutputStream.println("--END");
        responseOutputStream.flush();
    }

    // Print the ending boundary string
    responseOutputStream.println("--END--");
    responseOutputStream.flush();
    responseOutputStream.close();
}

The code that I’m using here comes from this stackoverflow post which links to this example post I also looked at this post as well to see if there was something wrong with the original links, and I couldn’t find anything.

I have tried adding the –END boundary to the top and bottom of each loop, to no success.

EDIT

I assume this has something to do with the file.getValue().writeTo(responseOutputStream); line, but from what I found I thought this would work.

EDIT 2

Firefox will allow me to download both files, but upon opening the files, there’s a notification stating “We found a problem with some content” and asks to recover. Clicking yes seems to show the proper information and states that some content may have been repaired or discarded. This doesn’t happen when writing to a file (FileOutputStream)

Here’s the response that chrome shows

--END
Content-type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
Content-Disposition: attachment; filename=Deductions.xlsx
PK˜`*S[Content_Types].xmlµSËnÂ0ü•È×*6ôPUCÇ©ô{“Xø%¯¡ð÷]8”R‰
qòcfgfWöd¶q¶ZCB|ÃÆ|Ä*ð*h㻆},^ê{Va–^K<4lÈfÓÉb+ªõØ°>çø ªœD"xBÚœÌtLˆR-eâv4º*ø>×¹h°éä  Z¹²¹zÜÝé†É­Q2S,±öúH´Þòvà`o"ÞUÏRÙµC(2q†Ãqa9SÝ
&
ÿŠÚÖ(ÐA­•p(ªtS6°Ï9—)¿JG‚‚ÈsBQ4¿Äû0œeXˆ9u‹1ÔØdg9ö2~ω^Óï+~®˜#oí‰)”rÍ ÐÊ4þ”ûWHËÏ–×ó/Ãþ/ûD1,ãC1|ïé7PK‘,(¼;PK˜`*S_rels/.rels­’ÁJ1†_%̽›miÚ‹½‰Ô“ÙÝ°›LHFݾ½Á‹¶lAÁã03ßÿ1Év?‡I½S.ž£uÓ‚¢hÙùØx9>®î@ÁèpâHNT`¿Û>Ó„RWÊàSQ•‹A$Ýk]ì@KÉbítœJ-s¯Ú{Ò›¶½Õù'Ιêàäƒ[ƒ:bîIÌ“þà<¾2MÅÖÆ)ÑoB¹ë¼¥¶o¢,d_L€^vÙ|»8¶O™ë&¦ôß24EGn•jeñõâWŒnŒ,gú›ÒõGÑ
~Q/„ôÙØ}PKn2KåJPK˜`*SdocProps/app.xmlMŽÁ
Â0Dï‚ÿro·z‘4¥ ‚'{ÐéÖšMHVé盓zœæñT·úE¼1e¨•»º‘ɆÑѳ•û¥:ÊNo7jH!bb‡Y”åVÎÌñíŒÞäºÌT–)$o¸Äô„0MÎâ9Ø—GbØ7Ípe¤Ç*~R«>ÆÅYÃEB÷ѤnWÿ½‚ŸƒþPK6nƒ!“¸PK˜`*SdocProps/core.xmlm]KÃ0†ÿJÈ}›¤êBÛ!Ê@PXQ¼É±-6$ÑnÿÞ´Î
ê]’÷9'o¹=è}€½5f9ÅŒ´ª7m›]vQˆÂ(1X>BÀÛº”ŽKëaï­{(yLàÒU¸‹ÑqB‚ì@‹'¤ðÕz-bºú–8!ßD¤ ôŒhˆB‰(È$ÌÜbÄ'¥’‹Ò½ûa(I`
&ÂrF~Ø^‡æd!¡_¨qóq5#Fžïnæå³ÞL—€ëò¤æÒƒˆ Pðxt©’ïäiuuÝìp]Ђet“1Ú°sN×|½y)ɯùIøu¶¾¾L…t€ö÷7·<—äOÍõ'PK-Ô˱PK˜`*Sxl/sharedStrings.xml…Ò]kƒ0àûÂþCȽM´kYE-²¥P¨Zfº±Ë ™
š¸|ŒíßϱA™
ìò<çÍ{ní>†¼s¥;)bè/1T²îDÃ3Ý{wp—Ü,"­
¨¤&†Ý›å÷˜j„ŽakÌ"¤«–L/åÈÅ´y•j`fUƒô¨8«u˹z`¼AëL"Ý%‘IJÔ   AyÎÁ&ĤقˆúÖáz;A„L¡ïøï+jö9×Lº”Z®üÌkáÐÖ*—ïUçÐ’«-Ò°~Ž©m¼`ëÒvªuŽvkàÔ•SogŠþDžÈãa €d§cñBH9/É‹Üû?uJéäôÊiAÓãÑôÓ’/PKÈ2‘—PK˜`*S
xl/styles.xmlíYËŽ›0ÝWê? ï;’L’
U#¥ê¦‹ÎŒÔ­†XõgJæëkc’*'ˆ–ªÙûrï90Ç%x()q^‘(0g!ÜyÀA,æ   fY^žç¦à!zÿ.(äš §%BÒQ¬ÁRÊü£ëñQXÜñ1u&å‚B©¦"s‹ ˜ºˆ×÷¼{—BÌ@°SY81_1¸Qr¶‹=`"QP¼9¯(mZœÊ‹9áÂÁ,A%JB0Õ1)2Yà…À ¤˜¬MØ×JjG1ãB]Ãb~âløFÀ­¢_3•b…nÿ1À¿¨ùïÜ*þü
«…„l·¼10(È¡”H°¹š8õøy£0Îj˜*ïLvÅÏ®í+
NpriºJË÷÷äQµ)»ÂÖcÿ(duPwsÁE¢™Íý€M(
J¥*8[ê£ä¹~6JNÕ Á0ãM°©¨
6F„<éîç{º‡]¦Žic¾$ºƒqô3Ý• zh`ÌDã7Ñv¶êz.ÇuÊtKp¬zp¾ÚyNÖs®•T6
ÔÂÎEL:?ÌŸQ)k¹LÓùWЙÀ§
åR  øÆ%”U³:SK.ð›‚Ð/Y¬R8+mØ_i£þJ÷WÚ}¥Mú+mÚ3i“´ÙNÚð´4•µ'íëŠ.˜Wß {'qÊ$-<öð=8éW<µ_w®ÄÚÊ;WbíÜ+±6êΕXûrçJ¬m¸s%“«ýÃÒ&Þ9ºŒ ókŸõVšïõWÚ5Ýñ櫓ùÖžy͆Õ$²¶Ä¶D֎זÈÚÐÚYûU["ë&°-‘uK×–¨óÍ­?ŒÕh÷ÇCôPK*ú-Š­PK˜`*Sxl/workbook.xmlŽÁNÃ0DïHüƒµwj§ Qœ^*¤Þ8i¬Æv´ë¶|>NªGN«Ñ¼™fóqAbŸ¢†j¥@`´ÉùxÔð±{xM{×)Dá#kèsk)Ùö¯Òˆ±8]¢`r‘t”<Ç=bƒ+õ,ƒñn    5ý'#u·¸Mö0æ[á`rY˽ڟeï$œÉX½ª'
A¶Íä|z¼ò/8IalöÜ›ƒ5qò8o^®ˆ& †-º³kAPíÚ¹G3³+²šS–W¹”µßPK™†¾ZÜbPK˜`*Sxl/_rels/workbook.xml.rels­‘MkÃ0@ÿŠÑ}qÒÁ£n/cÐk?~€°•84±¥µË¿¯»ÃÖ@;ô$Œð{´ƒ:Qæ>MUƒ¢`£ëCgà°ÿxzÅ‚Áᘈa½Zni@)_Ø÷‰Ua6àEÒ›Öl=ÈULʦyD)ÏÜé„öˆéE]¿è|Ë€9Smœ¼q
¨=æŽÄ{Ìäv’KWVS¢ÿhcÛö–Þ£ý)È»žÁAßYÜÄÈ4Ðã+¾©éŸõç˜ì‰äZ^Fóè’Á5FÏ®½ºPKg뢨Õ4PK˜`*Sxl/worksheets/sheet1.xmlu•[o›0Çß'í; ?m‡ @ÕÜ‘V©Z×홓 FÆIÚ}úù›e/‰/¿sáìãùÝ[]9gL»’41€#8¸Éɾl1xþ¹ù6wÉçOó¡¯Ýcæpƒ¦‹Á‘±6rÝ.?â:ëF¤Å
ß)­3Ƨôàv-ÅÙ^Õ•ë{ÞÄ­³²É|_Ö¸Š‹ÜÃ(…p“¹„•øÒcGÄ~!äULÒ}xŽ,{yÂÎæsFO˜{m³;ïOmU²øâK:–1ƒ‚’?¸áF¤ýŽ¶ÄUŃ"àd9+Ïø‘ÛÅà…0Fj±/‘ÎE†í]WçfŽûœ7RƒGêìq‘*öƒv¸<yV0ÉïÌIÕÉ_§.…ìÀ©³7ù)÷ìä‰üóSÇ£þVKò+Epe,C­2–%sJ.ÖÜ¥Üs?<XÇP«çÄ›»gaz%&áKÚÄÒ$$|›X™ÄXÈ&Ö&Hbl“˜H"°‰­I„’˜ØÄÎ$¦’m"5‰™$¦šp¹zZB¿—pá›zÒf6PÈB”Ìp óÊb®B”^[Œ’´ÞXŒÔÞZŒ’ôÞYŒO-FI'·õBúÈ!ÓHU†·Æâ_Çv˜T‘àª41„ÚAhiU18»í`j8˜cYèëòLwªºþ?·ÈSÛ¢°Eòôüðe¢ÅôëÜ-¬«tÅÆ–(Z±U5¶BÑjˆ­{,ÐØEë!¶é±‰Æ6(Ú±m…Û¢h;Äv=6ÕØE»!–öØìCèEÜúT…pöUczÀ¢)wNNN
SÇJ¯^ߥH”j¸Dipk=ŒÒP¶Ë÷â‰8à‡ŒÊ¦sTKç¯É(äg¯ „a*fü$ù»¥'oú’U]ŽùcrµAôó˜üPKÈýй½RPK˜`*S‘,(¼;[Content_Types].xmlPK˜`*Sn2KåJ|_rels/.relsPK˜`*S6nƒ!“¸šdocProps/app.xmlPK˜`*S-Ô˱kdocProps/core.xmlPK˜`*SÈ2‘—°xl/sharedStrings.xmlPK˜`*S*ú-Š­
xl/styles.xmlPK˜`*S™†¾ZÜbÈxl/workbook.xmlPK˜`*Sg뢨Õ4á  xl/_rels/workbook.xml.relsPK˜`*SÈýй½Rþ
xl/worksheets/sheet1.xmlPK      ?
--END
Content-type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
Content-Disposition: attachment; filename=Inventory.xlsx
PK˜`*S[Content_Types].xmlµSËnÂ0ü•È×*6ôPUCÇ©ô{“Xø%¯¡ð÷]8”R‰
qòcfgfWöd¶q¶ZCB|ÃÆ|Ä*ð*h㻆},^ê{Va–^K<4lÈfÓÉb+ªõØ°>çø ªœD"xBÚœÌtLˆR-eâv4º*ø>×¹h°éä  Z¹²¹zÜÝé†É­Q2S,±öúH´Þòvà`o"ÞUÏRÙµC(2q†Ãqa9SÝ
&
ÿŠÚÖ(ÐA­•p(ªtS6°Ï9—)¿JG‚‚ÈsBQ4¿Äû0œeXˆ9u‹1ÔØdg9ö2~ω^Óï+~®˜#oí‰)”rÍ ÐÊ4þ”ûWHËÏ–×ó/Ãþ/ûD1,ãC1|ïé7PK‘,(¼;PK˜`*S_rels/.rels­’ÁJ1†_%̽›miÚ‹½‰Ô“ÙÝ°›LHFݾ½Á‹¶lAÁã03ßÿ1Év?‡I½S.ž£uÓ‚¢hÙùØx9>®î@ÁèpâHNT`¿Û>Ó„RWàSQ•‹A$Ýk]ì@KÉbítœJ-s¯Ú{Ò›¶½Õù'Ιêàäƒ[ƒ:bîIÌ“þà<¾2MÅÖÆ)ÑoB¹ë¼¥¶o¢,d_L€^vÙ|»8¶O™ë&¦ôß24EGn•jeñõâWŒnŒ,gú›ÒõGÑ
~Q/„ôÙØ}PKn2KåJP˜`*SdocProps/app.xmlMŽÁ
Â0Dï‚ÿro·z‘4¥ ‚'{ÐéÖšMHVé盓zœæñT·úE¼1e¨•»º‘ɆÑѳ•û¥:ÊNo7jH!bb‡Y”åVÎÌñíŒÞäºÌT–)$o¸Äô„0MÎâ9Ø—GbØ7Ípe¤Ç*~R«>ÆÅYÃEB÷ѤnWÿ½‚ŸƒþPK6nƒ!“¸PK˜`*SdocProps/core.xmlm]KÃ0†ÿJÈ}›¤êBÛ!Ê@PXQ¼É±-6$ÑnÿÞ´Î
ê]’÷9'o¹=è}€½5f9ÅŒ´ª7m…›]vQˆÂ(1X>BÀÛº”ŽKëaï­{(yLàÒU¸‹ÑqB‚ì@‹'¤ðÕz-bºú–8!ßD¤ ôŒhˆB‰(È$ÌÜbÄ'¥’‹Ò½ûa(I`
&ÂrF~Ø^‡æd!¡_¨qóq5si#Fžïnæå³ÞL—€ëò¤æÒƒˆ Pðxt©’ïäiuuÝìp]Ђet“1Ú°sN×|½y)ɯùIøu¶¾¾L…t€ö÷7·<—äOÍõ'PK-Ô˱PK˜`*Sxl/sharedStrings.xmlmÒÁNÃ0àû$Þ!ʽKÖ±i«ÒLb'‡vÚ9jM©MJâ x{‚@š9úóÛ‡ˆÃÇ4’wp^[SÒÕ’S¦µ6}IÏÍ1Ûу¼[ï‘´6Œ¡%Áè·WˆcŒ/é€8Œùv€Iù¥ÁÄΫu“ÂXºžùÙêü€ÓÈrηlRÚP)¼–eÊaAêó3Ùœ“ê´x4ÝlŠÍ>‚`(ûŽÿ>  ¦SŸ·z²)mø_ 3ÉF3—ò£Ó ­—šbQ·X…>Ë÷)]ó¤®þ­ƒ9ãiÍ“ºNêý²¿·¿4ÕÓ5ÁâO_PK©Cxð7PK˜`*S
xl/styles.xmlí˜ËŽ›0†÷•ú–÷&Í¥FÕH©ºé¢“JÝ:`ˆU_q¦dž¾6&  ©2  Q5›`Îùýaà·‰÷3
ž±Ìˆà>ß À<ቬ–æð!xÿÎËÔŽâ§
Æ
è
žùp£TúÉq²pƒÊîDŠ¹>ÉÒ]™8Y*1Š2SĨãŽFS‡!Âaàñ-[2•Pl¹òá:~Œ¸3h#—½€gD5›Óy¡ BÂ#œãˆa›õˆ(YKR"FèΆ](PË<F¸&èØQìïY€knêXÛ®’[|øþEæ›ÀpŠC¦…¥‡…im ðR¤–|©; l¯v)ö!¼”)ò®dGHþú"Ñ®~E&(‰Þš®Ó’ÇÓ•sR,N¥ð Yô¥¯…ŒôÞ`ñc¸Å±Òå’$sT"5)”L7"‚Á5ì+ʆ–
1¥OfCñ3>ÑÎc`w_#³)æ웨lZÛ1úU5«]‘-6o×y|àµêñõj€Ò”î–ÂOWàé§0ás~K”®p®*^¿>œÛ`8ø¼ò„Òß…BªØÿ-´ÆFHò¢%Ìê,¯¢Ým2´ÃE›m6´ùÀÐfG´Åíþ2šÎ:Aû¶ek,—ÅgÝɉÊE2É{~.zgƒ»3®íדԶòÎIj;wç$µºs’Ú¾Ü9ImîœdÖØ?jÚDïü[oÑ[´›º:®×dN+ˆnKgì±¥eöØÒKû@li²} ¶tß>[ÚrˆÓÆ.Ù9³S~ëÖñ¯üàPKn)óxÿPK˜`*Sxl/workbook.xmlŽÁNÃ0†ïH¼Cäû–”!UÓ]Row/u×hMR9¡ƒ·'íTàÈÉúåÏ¿¿jÿé1G¼†b«@7¡µþ¤áíð²y„}}{S]Ÿ!œEæ}ÔЧ4–RFӓø
#ù¼é;L9òIÆ‘ ÛØ%7È;¥¤CëáÚPò:B×YCÏÁ|8òéZÂ4`ʶ±·c„úÇì•E‹‰Š'u¯¡Ã!Ⱥš7ï–.ñœ£@“ìD<jP3'ÿ€‹ó:…GG?eƒÀ_ ¸´­nÚˆir,–’õR®¿êoPKîÓH¥ÝaPK˜`*Sxl/_rels/workbook.xml.rels­‘MkÃ0@ÿŠÑ}qÒÁ£n/cÐk?~€°•84±¥µË¿¯»ÃÖ@;ô$Œð{´ƒ:Qæ>MUƒ¢`£ëCgà°ÿxzÅ‚Áᘈa½Zni@)_Ø÷‰Ua6àEÒ›Öl=ÈULʦyD)ÏÜé„öˆéE]¿è|Ë€9Smœ¼q
¨=æŽÄ{Ìäv’KWVS¢ÿhcÛö–Þ£ý)È»žÁAßYÜÄÈ4Ðã+¾©éŸõç˜ì‰äZ^Fóè’Á5FÏ®½ºPKg뢨Õ4PK˜`*Sxl/worksheets/sheet1.xmlmÔ[o›0àûIû–¯¶‹…ƒIÒ" j„H«T­ëví“ FÆMÚýúù@©mõ&±áùló’|ÉÍkÛ€3aCM»3Ò•ôPwÇ>þÎÁ›ìë—äBÙóp"„QÐ
)<qÞÇž7”'ÒâaF{Ò‰;e-æbÊŽÞÐ3‚ª¨m¼Ð÷^‹ëfÉ¡nI'wŒT)¼
â}½,QöOM.ƒ1rë'JŸådH¡8"ÇO¤!%'bÎÙ‹ö¸#àí¡ojžÂP>ÈÀ1')¬ýG:QDûŸ¤âkÒ4bO.y}&÷¢.…O”sÚÊûê jqyÀþ“»Þt6sü~æEpÏÀTø¥á¿è¥ õñ$NÌűÄ%mõ  ÚZ¦A‹_Õ÷¥>ðS
‘/Ï_¾b׿ú’zJ¹¹.V[m0ÇYÂè0Y-–”ƒ[±ŽØlè«çÌO¼³,ÅÊ¡-Ö¦@J„¶Ø˜"RÙbkŠ¹‘-rS,”˜ÛbgŠ¥[¦¸Rbi‹½)®•¸š„'Ò›"ß#…f„¾ª¹v²ˆŽ9prÞXfÚIzkuàd[F‡8iï,£ãœ¼ËèÀ'ñ½etäÁâó¼üAŽãhúùEæúÎ+YEú®|Uöðx÷m…âUø=ñ*3b­BRk¯]µU0©
Š7®ÚŽ*œÔÅ[Wå£B“ÊQœ»j7ªhR;ïUŒj>©Å…«ö£Z|$ÅEô¡tàžñïñ‘Üav¬»èÆ$zâl9‡ ¢”&g¢»Dó&h]JAÀt/RcÑÇZÙY¦ŸýPKl"¬tiPK˜`*S‘,(¼;[Content_Types].xmlPK˜`*Sn2KåJ|_rels/.relsPK˜`*S6nƒ!“¸šdocProps/app.xmlPK˜`*S-Ô˱kdocProps/core.xmlPK˜`*S©Cxð7°xl/sharedStrings.xmlPK˜`*Sn)óxÿ
âxl/styles.xmlPK˜`*SîÓH¥Ýa•xl/workbook.xmlPK˜`*Sg뢨Õ4¯ xl/_rels/workbook.xml.relsPK˜`*Sl"¬tiÌ
xl/worksheets/sheet1.xmlPK      ?{
--END
--END--

Chrome just shows a black page with a white square in the middle that it thinks is an image (right clicking it shows save image options) Chrome display after response

Answer

So I found the reason why this wasn’t working on chrome but it was working on firefox.

Chromium removed support for x-mixed-support and multiple file downloads per request, according to this update.

I took the advice of other posts I saw (can’t find the links now) to make JavaScript open two new tabs for each download.

Zipping the files is also an option, but is a little less convenient for my use case.