为什么 chrome 不下载响应中包含的文件?

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

我目前 运行 一个 Java Spring 引导程序,它应该 return 一个包含两个 Excel 文件 (.xlsx) 作为 ByteArrayOutputStream 的响应在 ServletOutputStream 中作为 Content-Disposition: attachment;

但是,在 return 对 chrome 的响应中,它没有下载任何内容。

Excel 文件是使用 Apache POI 生成的,然后将它们写入 ByteArrayOutputStream。

这是我生成文件的代码

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

这是我处理请求和 return 响应的端点。

@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();
}

我在这里使用的代码来自this Whosebug post which links to this example post 我也查看了 this post 以查看原始链接是否有问题,但我找不到任何内容。

我尝试将 --END 边界添加到每个循环的顶部和底部,但没有成功。

编辑

我认为这与 file.getValue().writeTo(responseOutputStream); 有关线,但根据我的发现,我认为这会起作用。

编辑 2

Firefox 将允许我下载这两个文件,但在打开文件时,会出现一条通知,指出“我们发现某些内容有问题”并要求恢复。单击“是”似乎显示了正确的信息并声明某些内容可能已被修复或丢弃。写入文件时不会发生这种情况 (FileOutputStream)

这是 chrome 显示的响应

--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è/1\T²î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[ê£ä¹~6\JNÕ Á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’KW\VS¢ÿ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ÝɉÊEÉ{~.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’KW\VS¢ÿ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 只显示一个黑色页面,中间有一个白色方块,它认为这是一张图片(右键单击它会显示保存图片选项)

您正在直接返回多个文件。要响应多个文件,请压缩它们并将它们作为 zip 文件发送。同样在代码中,您首先设置内容类型,然后更新 contentType。

    // 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";

您可以查看以下内容link,了解如何在响应中返回多个文件。

所以我找到了为什么这在 chrome 上不起作用但它在 firefox 上起作用的原因。

根据 this update,Chromium 删除了对 x-mixed-support 和每个请求的多个文件下载的支持。

我听取了我看到的其他帖子(现在找不到链接)的建议 JavaScript 为每次下载打开两个新标签。

压缩文件也是一种选择,但对我的用例来说不太方便。