我一直使用POI给Excel写图片ImageIO.write(bufferImg, pictureFormat, byteArrayOut);,今天遇见一个奇葩的问题,导出这个Excel在本地一直都是好的,课时到了正式的Linux的环境下,却打死都倒不出来。
javax.imageio.IIOException: Can't create output stream! javax.imageio.ImageIO.write(ImageIO.java:1521)
org.apache.jsp.images_jsp._jspService(images_jsp.java:126) org.apache.jasper.runtime.HttpJspBase.service(HttpJspBase.java:94)
javax.servlet.http.HttpServlet.service(HttpServlet.java:802)
org.apache.jasper.servlet.JspServletWrapper.service(JspServletWrapper.java:324)
org.apache.jasper.servlet.JspServlet.serviceJspFile(JspServlet.java:292)
org.apache.jasper.servlet.JspServlet.service(JspServlet.java:236)
javax.servlet.http.HttpServlet.service(HttpServlet.java:802)
org.extremecomponents.table.filter.AbstractExportFilter.doFilter(AbstractExportFilter.java:49)
</pre><p></p><pre name="code" class="java">javax.imageio.IIOException: Can't create output stream!
at javax.imageio.ImageIO.write(ImageIO.java:1521)
at org.apache.jsp.images_jsp._jspService(images_jsp.java:126)
at org.apache.jasper.runtime.HttpJspBase.service(HttpJspBase.java:94)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:802)
at org.apache.jasper.servlet.JspServletWrapper.service(JspServletWrapper.java:324)
at org.apache.jasper.servlet.JspServlet.serviceJspFile(JspServlet.java:292)
at org.apache.jasper.servlet.JspServlet.service(JspServlet.java:236)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:802)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:237)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:157)
at org.extremecomponents.table.filter.AbstractExportFilter.doFilter(AbstractExportFilter.java:49)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:186)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:157)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:214)
at org.apache.catalina.core.StandardValveContext.invokeNext(StandardValveContext.java:104)
at org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:520)
at org.apache.catalina.core.StandardContextValve.invokeInternal(StandardContextValve.java:198)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:152)
at org.apache.catalina.core.StandardValveContext.invokeNext(StandardValveContext.java:104)
at org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:520)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:137)
at org.apache.catalina.core.StandardValveContext.invokeNext(StandardValveContext.java:104)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:118)
at org.apache.catalina.core.StandardValveContext.invokeNext(StandardValveContext.java:102)
at org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:520)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109)
at org.apache.catalina.core.StandardValveContext.invokeNext(StandardValveContext.java:104)
at org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:520)
at org.apache.catalina.core.ContainerBase.invoke(ContainerBase.java:929)
at org.apache.coyote.tomcat5.CoyoteAdapter.service(CoyoteAdapter.java:160)
at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:799)
at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.processConnection(Http11Protocol.java:705)
at org.apache.tomcat.util.net.TcpWorkerThread.runIt(PoolTcpEndpoint.java:577)
at org.apache.tomcat.util.threads.ThreadPool$ControlRunnable.run(ThreadPool.java:683)
at java.lang.Thread.run(Thread.java:595)
Caused by: javax.imageio.IIOException: Can't create cache file!
at javax.imageio.ImageIO.createImageOutputStream(ImageIO.java:395)
at javax.imageio.ImageIO.write(ImageIO.java:1519)
... 34 more
Caused by: java.io.IOException: 系统找不到指定的路径。
at java.io.WinNTFileSystem.createFileExclusively(Native Method)
at java.io.File.checkAndCreate(File.java:1345)
at java.io.File.createTempFile(File.java:1434)
at javax.imageio.stream.FileCacheImageOutputStream.<init>(FileCacheImageOutputStream.java:67)
at com.sun.imageio.spi.OutputStreamImageOutputStreamSpi.createOutputStreamInstance(OutputStreamImageOutputStreamSpi.java:50)
at javax.imageio.ImageIO.createImageOutputStream(ImageIO.java:391)
... 35 more
原来是ImageIO.write(image, "jpeg", response.getOutputStream());
需要写缓冲文件,但是写文件不成功。
解决办法
1.可以 修改程序
把 ImageIO.write(image, "jpeg", response.getOutputStream());
修改为
JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(response.getOutputStream());
encoder.encode(image);
2.tomcat 的temp 目录必须有
为什么 ImageIO.write 这种方式 需要 temp 目录 呢?
带着疑问,看看源码(Java的开源好处真心不错) :
- public static ImageOutputStream <strong>createImageOutputStream</strong>(Object output)
- throws IOException {
- if (output == null) {
- throw new IllegalArgumentException("output == null!");
- }
- Iterator iter;
- // Ensure category is present
- try {
- iter = theRegistry.getServiceProviders(ImageOutputStreamSpi.class,
- true);
- } catch (IllegalArgumentException e) {
- return null;
- }
- boolean usecache =<strong> getUseCache() && hasCachePermission()</strong>;
- while (iter.hasNext()) {
- ImageOutputStreamSpi spi = (ImageOutputStreamSpi)iter.next();
- if (spi.getOutputClass().isInstance(output)) {
- try {
- return spi.createOutputStreamInstance(output,
- usecache,
- <strong>getCacheDirectory()</strong>);
- } catch (IOException e) {
- throw new IIOException("Can't create cache file!", e);
- }
- }
- }
- return null;
- }
会调用 cache 文件夹 ,他所谓的cache 文件夹是怎么样定义的呢?
- private static boolean hasCachePermission() {
- Boolean hasPermission = getCacheInfo().getHasPermission();
- if (hasPermission != null) {
- return hasPermission.booleanValue();
- } else {
- try {
- SecurityManager security = System.getSecurityManager();
- if (security != null) {
- File cachedir = getCacheDirectory();
- String cachepath;
- if (cachedir != null) {
- cachepath = cachedir.getPath();
- } else {
- cachepath = getTempDir();
- if (cachepath == null) {
- getCacheInfo().setHasPermission(Boolean.FALSE);
- return false;
- }
- }
- security.checkWrite(cachepath);
- }
- } catch (SecurityException e) {
- getCacheInfo().setHasPermission(Boolean.FALSE);
- return false;
- }
- getCacheInfo().setHasPermission(Boolean.TRUE);
- return true;
- }
- }
写得也很清楚,如果自己设置了 set
CacheDirectory 那么会使用自定义的,否则调用
getTempDir()
- /**
- * Returns the default temporary (cache) directory as defined by the
- * java.io.tmpdir system property.
- */
- private static String getTempDir() {
- GetPropertyAction a = new GetPropertyAction("java.io.tmpdir");
- return (String)AccessController.doPrivileged(a);
- }
Oh, 会读取变量java.io.tmpdir!
大家还记得 tomcat 启动的时候,会显示
- Using CATALINA_BASE: /home/appuser/appservers/tomcat-feilong
- Using CATALINA_HOME: /home/appuser/appservers/tomcat-feilong
- Using CATALINA_TMPDIR: /home/appuser/appservers/tomcat-feilong/temp
- Using JRE_HOME: /usr/lib/jvm/java-6-sun
- Using CLASSPATH: /home/appuser/appservers/tomcat-feilong/bin/bootstrap.jar
$CATALINA_TMPDIR 这个变量就是 我们的temp 文件夹
而tomcat 启动的时候,调用jvm会设置 java.io.tmpdir 参数
看
rem
CATALINA_TMPDIR (Optional) Directory path location of temporary directory
rem the JVM should use (java.io.tmpdir). Defaults to
rem %CATALINA_BASE%\temp.
%_EXECJAVA% %JAVA_OPTS% %CATALINA_OPTS% %DEBUG_OPTS% -Djava.endorsed.dirs="%JAVA_ENDORSED_DIRS%" -classpath "%CLASSPATH%" -Dcatalina.base="%CATALINA_BASE%" -Dcatalina.home="%CATALINA_HOME%" -Djava.io.tmpdir="%CATALINA_TMPDIR%" %MAINCLASS% %CMD_LINE_ARGS% %ACTION%
这下终于弄明白了, ImageIO.write(image, "JPEG", os) 这种方式 来生成图片、验证码 tomcat必须要有temp文件夹的来龙去脉了