从数据库导出Excel上线后出现IO异常:设备未就绪之解决方法

时间:2024-04-06 15:32:22

最近做项目遇到一个问题,就是利用JavaPOI导出Excel表格时,在自己电脑上的工程上面可以使用,但是项目一上线,这个功能就报错,错误如下:

从数据库导出Excel上线后出现IO异常:设备未就绪之解决方法

设备未就绪异常,报错的语句是createNewFile()这个方法出的错,让我一头雾水,这个方法用的好多次从来也没有报过这种错,上网查很多人都说是创建文件的路径有问题,但是我这个很明显不是这个问题,要是路径有问题的话,在我自己电脑里的工程中也会出现这种问题的,所以我猜想,上线之后和上线之前有什么不一样。后来问过同事才知道问题出在那里了,先贴出我出问题时的代码。(用的SSH框架以下是Action中的代码)

public String exportJiuYeByWorkTime() {
        HttpServletResponse response=ServletActionContext.getResponse();
        response.setCharacterEncoding("utf-8");
        List list=excelExportService.exportJiuYeByWorkTime(worktimeStart, worktimeEnd);
        boolean b=false;
        String fileName="D://"+worktimeStart+"-"+worktimeEnd+"就业表.xlsx";
        File file=new File(fileName);
        FileOutputStream fout=null;
        try{
            if(file.exists()) {
                file.delete();
            }
            if(!file.exists()) {
                file.createNewFile();
            }
            //创建一个以fileName为文件名的workbook
            XSSFWorkbook workbook=new XSSFWorkbook();
            //创建一个名字是传进来的班级名字的表
            XSSFSheet sheet = workbook.createSheet(worktimeStart+"-"+worktimeEnd+"就业表");
            XSSFRow row = null;
            row = sheet.createRow(0);
            XSSFCell cell=null;
            cell=row.createCell(0);
            cell.setCellValue("班级");
            cell=row.createCell(1);
            cell.setCellValue("姓名");
            cell=row.createCell(2);
            cell.setCellValue("入职日期");
            cell=row.createCell(3);
            cell.setCellValue("薪资");
            cell=row.createCell(4);
            cell.setCellValue("入职企业");
            cell=row.createCell(5);
            cell.setCellValue("入职岗位");
            cell=row.createCell(6);
            cell.setCellValue("就业经理");
            for (int i = 1; i <= list.size(); i++) {
                row=sheet.createRow(i);
                cell=row.createCell(0);
                cell.setCellValue(((JiuYeInfo)list.get(i-1)).getJibenInfo().getClassname());
                cell=row.createCell(1);
                cell.setCellValue(((JiuYeInfo)list.get(i-1)).getJibenInfo().getName());
                cell=row.createCell(2);
                cell.setCellValue(((JiuYeInfo)list.get(i-1)).getWorktime());
                cell=row.createCell(3);
                cell.setCellValue(((JiuYeInfo)list.get(i-1)).getWorksalary());
                cell=row.createCell(4);
                cell.setCellValue(((JiuYeInfo)list.get(i-1)).getWorkcompany());
                cell=row.createCell(5);
                cell.setCellValue(((JiuYeInfo)list.get(i-1)).getWorkpost());
                cell=row.createCell(6);
                cell.setCellValue(((JiuYeInfo)list.get(i-1)).getWorkpost());
                cell=row.createCell(7);
                cell.setCellValue(((JiuYeInfo)list.get(i-1)).getEmploManager());
            }
            fout=new FileOutputStream(file);
            workbook.write(fout);
            b=true;
        }catch(Exception e) {
            e.printStackTrace();
        }finally {
            try {
                fout.flush();
                fout.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        if(b) {
            try {
                PrintWriter out = response.getWriter();
                out.write("Excel导出成功");
            } catch (IOException e) {
                e.printStackTrace();
            }
        }else {
            try {
                PrintWriter out = response.getWriter();
                out.write("Excel导出失败");
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return null;
    }

这段代码看似没有问题,实际上在本地的工程中它也是没有问题的,但是一上线就会出错,因为我在这段代码上直接就在本地D盘创建了一个文件,然后就想把数据传化的Excel表存到本地,这是不可能的。

 String fileName="D://"+worktimeStart+"-"+worktimeEnd+"就业表.xlsx";

当执行这句话时会在上线的项目会在服务器的D盘中创建一个文件,然后数据也始终都是在服务器端,整个导出的过程都是在服务器端完成,说实话和客户端完全没关系。那为什么Excel表中数据上传到数据库中则可以呢?

那是因为你利用input的file标签读取数据然后他会有一个请求的过程,所以数据会随着请求到达服务器端,而数据库也在服务器端,所以可以进行,当导出时没有一个响应的过程,数据一直存在于服务端,所以不会出现像导入那样顺利。

那么如何解决呢?

很简单只要多加一步将从数据库中的数据以下载的方式下载到本地就可以了。

改完之后的代码是:

public InputStream getJiuYeByWorkTime() {
        List list=excelExportService.exportJiuYeByWorkTime(worktimeStart, worktimeEnd);
        XSSFWorkbook workbook=new XSSFWorkbook();
        //创建一个名字是传进来的班级名字的表
        XSSFSheet sheet = workbook.createSheet(worktimeStart+"-"+worktimeEnd+"就业表");
        XSSFRow row = null;
        row = sheet.createRow(0);
        XSSFCell cell=null;
        cell=row.createCell(0);
        cell.setCellValue("班级");
        cell=row.createCell(1);
        cell.setCellValue("姓名");
        cell=row.createCell(2);
        cell.setCellValue("入职日期");
        cell=row.createCell(3);
        cell.setCellValue("薪资");
        cell=row.createCell(4);
        cell.setCellValue("入职企业");
        cell=row.createCell(5);
        cell.setCellValue("入职岗位");
        cell=row.createCell(6);
        cell.setCellValue("就业经理");
        for (int i = 1; i <= list.size(); i++) {
            row=sheet.createRow(i);
            cell=row.createCell(0);
            cell.setCellValue(((JiuYeInfo)list.get(i-1)).getJibenInfo().getClassname());
            cell=row.createCell(1);
            cell.setCellValue(((JiuYeInfo)list.get(i-1)).getJibenInfo().getName());
            cell=row.createCell(2);
            cell.setCellValue(((JiuYeInfo)list.get(i-1)).getWorktime());
            cell=row.createCell(3);
            cell.setCellValue(((JiuYeInfo)list.get(i-1)).getWorksalary());
            cell=row.createCell(4);
            cell.setCellValue(((JiuYeInfo)list.get(i-1)).getWorkcompany());
            cell=row.createCell(5);
            cell.setCellValue(((JiuYeInfo)list.get(i-1)).getWorkpost());
            cell=row.createCell(6);
            cell.setCellValue(((JiuYeInfo)list.get(i-1)).getWorkpost());
            cell=row.createCell(7);
            cell.setCellValue(((JiuYeInfo)list.get(i-1)).getEmploManager());
        }
        ByteArrayOutputStream baos = new ByteArrayOutputStream();  
        try {  
            workbook.write(baos);  
        } catch (IOException e) {  
            // TODO Auto-generated catch block  
            e.printStackTrace();  
        }  
        byte[] ba = baos.toByteArray();  
        ByteArrayInputStream bais = new ByteArrayInputStream(ba);  
        return bais; 
    }

以上是Action中的代码,因为使用的是Struts2,所以下载是用配置文件形式进行的,在Action中只需要提供一个输入流(含要传输的数据的流)即可剩下的就是在struts.xml文件中进行。

struts.xml文件中的写法是:

<action name="exportJiuYeByWorkTime" class="excelExportAction" ><!-- method="exportJiuYeByClassName" -->
                <result name="success" type="stream">
                    <param name="contentType">application/vnd.openxmlformats-officedocument.spreadsheetml.sheet</param>
                    <param name="inputName">jiuYeByWorkTime</param><!-- 表示一个流  必须是一个输入流Action中的流-->
                    <param name="contentDisposition">attachment;filename="jiuYeByWorkTimePeople.xlsx"</param><!-- 弹出的对话框,以及 文件名自定义的名字-->
                    <param name="bufferSize">4096</param><!--byte数组的大小(其实这个配置文件就是封装了一个流的上传下载的代码)-->
                </result>
            </action>

这样的话就将直接在本地创建文件,转换成以下载的形式获得文件。