java 发邮件带excel附件,以流的形式发送附件,不生成excel文件

公司需求:根据业务需要定时发送邮件,并且邮件需要有excel附件,附件内容为查询到的数据。

开发思路:由于服务器环境本身空间有限,而且不确定环境中读写权限,生成excel文件比较浪费资源,出现异常时可能导致删除临时文件失败。因此生成excel文件时不生成实际文件,直接获取到excel写入流,并且将流直接写入到邮件附件中。

查询了很多网上资料以及java邮件API,最终得以实现,分享下供大家参考,互相学习!

转载请标明出处:https://blog.csdn.net/yixin605691235/article/details/82429156

 

详细代码:https://download.csdn.net/download/yixin605691235/10648074

核心代码如下:

配置邮箱服务器端口及发送人接收人

mail.smtp.host=smtp.exmail.qq.com
mail.smtp.port=465
[email protected]
mail.send.password=xxxxxx
[email protected]
[email protected],[email protected]

生成excel工具类

package com.yx.mail;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.net.URLEncoder;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.beanutils.PropertyUtils;
import org.apache.commons.lang3.StringUtils;

import jxl.Workbook;
import jxl.common.Logger;
import jxl.write.Label;
import jxl.write.Number;
import jxl.write.NumberFormat;
import jxl.write.WritableCell;
import jxl.write.WritableCellFormat;
import jxl.write.WritableSheet;
import jxl.write.WritableWorkbook;
import jxl.write.WriteException;
/**
 * java excel api(jxl) 导出excel工具类
 * @author Yanyanhui
 *
 */
public class JxlUtil {
    
    private static Logger logger = Logger.getLogger(JxlUtil.class);
    
    private static int MAX_SHEET_SIZE = 65530;
    
    private static String DOUBLE_FORMAT = "#.00";
    private static String INTER_FORMAT = "#";
    
    private static WritableCellFormat INTER_CELL_FORMAT = new WritableCellFormat(new NumberFormat(INTER_FORMAT));
    private static WritableCellFormat DOUBLE_CELL_FORMAT = new WritableCellFormat(new NumberFormat(DOUBLE_FORMAT));

    /**
     * 列表转换成excel导出
     * @param list 要转换的列表
     * @param title 转换列表的标题
     * @param sheetName 工作表名称
     * @param request 请求
     * @param response 响应
     * @throws Exception
     */
    public static  void listToExcel(List list,LinkedHashMap title,String sheetName,HttpServletRequest request,HttpServletResponse response) throws Exception{
        listToExcel(list, title, sheetName, sheetName, 0, 0, request, response);
        
    }
    
    /**
     * 列表转换成excel导出
     * @param list 要转换的列表
     * @param title 转换列表的标题
     * @param sheetName 工作表名称
     * @param fileName excel文件名称
     * @param request 请求
     * @param response 响应
     * @throws Exception
     */
    public static  void listToExcel(List list,LinkedHashMap title,String sheetName, String fileName,HttpServletRequest request,HttpServletResponse response) throws Exception{
        listToExcel(list, title, sheetName, fileName, 0, 0, request, response);
        
    }
    
    /**
     * 列表转换成excel导出
     * @param list 要转换的列表
     * @param title 转换列表的标题
     * @param sheetName 工作表名称
     * @param fileName excel文件名称
     * @param startRow 从哪行开始输出
     * @param request 请求
     * @param response 响应
     * @throws Exception
     */
    public static  void listToExcel(List list,LinkedHashMap title,String sheetName, String fileName,int startRow,HttpServletRequest request,HttpServletResponse response) throws Exception{
        listToExcel(list, title, sheetName, fileName, 0, startRow, request, response);
    }
    
    /**
     * 列表转换成excel导出
     * @param list 要转换的列表
     * @param title 转换列表的标题
     * @param sheetName 工作表名称
     * @param fileName excel文件名称
     * @param sheetSize 工作表的大小
     * @param startRow 从哪行开始输出
     * @param request 请求
     * @param response 响应
     * @throws Exception
     */
    public static  void listToExcel(List list,LinkedHashMap title,String sheetName, String fileName,int sheetSize,int startRow,HttpServletRequest request,HttpServletResponse response) throws Exception{

        if (list == null || list.size() == 0) {
//            throw new Exception("列表中没有任何数据");
        }

        if (sheetSize > MAX_SHEET_SIZE || sheetSize < 1) {
            sheetSize = MAX_SHEET_SIZE;
        }

        try {
            
            setResp(request, response, fileName);

            OutputStream outputStream = response.getOutputStream();

            WritableWorkbook wwb = Workbook.createWorkbook(outputStream);
            int size = 0;
            
            
            // 因为2003的Excel一个工作表最多可以有65536条记录,除去列头剩下65530条
            // 所以如果记录太多,需要放到多个工作表中,其实就是个分页的过程
            // 1.计算一共有多少个工作表
            double sheetNum = Math.ceil(size / new Integer(sheetSize).doubleValue());
            // 2.创建相应的工作表,并向其中填充数据

            WritableSheet sheet = wwb.createSheet(sheetName, 0);
            fillVeticalSheet(sheet, list, title, startRow, 0);
            
//            setColumnAutoSize(sheet, 2);
            
            wwb.write();
            wwb.close();

        } catch (WriteException e) {
            e.printStackTrace();
            logger.error(e.getMessage());
        } catch (IOException e) {
            e.printStackTrace();
            logger.error(e.getMessage());
        } catch (Exception e) {
            e.printStackTrace();
            logger.error(e.getMessage());
        }
    }
    
    
    /**
     * 以流的形式导出excel
     * @param list 要转换的列表
     * @param title 转换列表的标题
     * @param sheetName 工作表名称
     * @param fileName excel文件名称
     * @param sheetSize 工作表的大小
     * @param startRow 从哪行开始输出
     * @param request 请求
     * @param response 响应
     * @return 
     * @throws Exception
     */
    public static  ByteArrayOutputStream listToExcelReturnOut(List list,LinkedHashMap title,String sheetName, String fileName,int sheetSize,int startRow,HttpServletRequest request,HttpServletResponse response) throws Exception{

        if (list == null || list.size() == 0) {
//            throw new Exception("列表中没有任何数据");
        }

        if (sheetSize > MAX_SHEET_SIZE || sheetSize < 1) {
            sheetSize = MAX_SHEET_SIZE;
        }

//        OutputStream outputStream = response.getOutputStream();
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        setResp(request, response, fileName);
        try {

            WritableWorkbook wwb = Workbook.createWorkbook(outputStream);
            int size = 0;
            
            
            // 因为2003的Excel一个工作表最多可以有65536条记录,除去列头剩下65530条
            // 所以如果记录太多,需要放到多个工作表中,其实就是个分页的过程
            // 1.计算一共有多少个工作表
            double sheetNum = Math.ceil(size / new Integer(sheetSize).doubleValue());
            // 2.创建相应的工作表,并向其中填充数据

            WritableSheet sheet = wwb.createSheet(sheetName, 0);
            fillVeticalSheet(sheet, list, title, startRow, 0);
            
//            setColumnAutoSize(sheet, 2);
            
            wwb.write();
            wwb.close();
            
            
        } catch (WriteException e) {
            e.printStackTrace();
            logger.error(e.getMessage());
        } catch (IOException e) {
            e.printStackTrace();
            logger.error(e.getMessage());
        } catch (Exception e) {
            e.printStackTrace();
            logger.error(e.getMessage());
        }
        return outputStream;
    }
    


    
    /**
     * 填充竖直方向的sheet
     * @param sheet
     * @param data
     * @param title
     * @param row
     * @param col
     * @throws Exception
     */
    public static  void fillVeticalSheet(WritableSheet sheet, List data, LinkedHashMap title, int row,
            int col) throws Exception {
        if (null == data || null == title) {
            return;
        }

        // 定义存放英文字段名和中文字段名的数组
        String[] enFields = new String[title.size()];
        String[] cnFields = new String[title.size()];

        // 填充数组
        int count = 0;
        for (Entry entry : title.entrySet()) {
            enFields[count] = entry.getKey();
            cnFields[count] = entry.getValue();
            count++;
        }

        
        // 填充表头
        for (int i = 0; i < cnFields.length; i++) {
            String cnValue=cnFields[i];
            Label label = new Label(col + i, row,cnValue);
            sheet.addCell(label);
            sheet.setColumnView(col+i, cnValue.getBytes().length);//一个中文占2个字节,经过调试+4后效果比较理想 
    
        }

        // 填充内容
        int rowNo = row + 1;
        int colNo = col;

        for (int i = 0; i < data.size(); i++) {
            
            T item = data.get(i);
            for (int j = 0; j < enFields.length; j++) {
                Object objValue = getFieldValueByNameSequence(enFields[j], item);
                
                WritableCell cell = getWritableCell(colNo+j, rowNo, objValue);
                sheet.addCell(cell);
                
                
            }

            rowNo++;
        }
    }
    
    public static WritableCell getWritableCell(int column, int row, Object object){
        if (object == null) {
            return new Label(column, row, "");
        }
        
        String tempObj = object.toString();
        
        if (object instanceof Integer) {
            if (tempObj.equals("0")) {
                return new Number(column, row, 0);
            }

            return new Number(column, row, Double.valueOf(tempObj),INTER_CELL_FORMAT);
        }
        
        
        if (object instanceof Double) {
            return new Number(column, row, Double.valueOf(tempObj),DOUBLE_CELL_FORMAT);

        }
        
        if (object instanceof BigDecimal) {
            if (tempObj.indexOf(".00") > 0) {
                tempObj = tempObj.substring(0, tempObj.indexOf("."));
                return tempObj.equals("0") ? new Number(column, row, 0)
                        : new Number(column, row, Double.valueOf(tempObj),INTER_CELL_FORMAT);
            }

            return new Number(column, row, Double.valueOf(tempObj),DOUBLE_CELL_FORMAT);
        }
        
        return new Label(column, row, tempObj);
        
    }
    
    

    /**
     * 根据带路径或不带路径的属性名获取属性值,即接受简单属性名,如userName等,又接受带路径的属性名,如student.department.
     * name等
     *
     * @param fieldNameSequence
     *            带路径的属性名或简单属性名
     * @param o
     *            对象
     * @return 属性值
     * @throws Exception
     *             异常
     */
    public static Object getFieldValueByNameSequence(String fieldNameSequence, Object o) throws Exception {

        Object value = null;

        // 将fieldNameSequence进行拆分
        String[] attributes = fieldNameSequence.split("\\.");
        if (attributes.length == 1) {
            value = PropertyUtils.getProperty(o, fieldNameSequence);
            
        } else {
            // 根据属性名获取属性对象
            Object fieldObj = PropertyUtils.getProperty(o, attributes[0]);
        
            
            String subFieldNameSequence = fieldNameSequence.substring(fieldNameSequence.indexOf(".") + 1);
            value = getFieldValueByNameSequence(subFieldNameSequence, fieldObj);
        }
        
        if (value instanceof Date) {
            value = new SimpleDateFormat("yyyy-MM-dd").format(
                    value).toString();    
            }
        
//        if (value.toString().endsWith(".0000")) {
//            String txt =value.toString();
//            value = txt.substring(0, txt.lastIndexOf("."));
//        }
        
        return value;
    }

    /**
     * 根据字段名获取字段
     *
     * @param fieldName
     *            字段名
     * @param clazz
     *            包含该字段的类
     * @return 字段
     */
    public static Field getFieldByName(String fieldName, Class clazz) {
        // 拿到本类的所有字段
        Field[] selfFields = clazz.getDeclaredFields();

        // 如果本类中存在该字段,则返回
        for (Field field : selfFields) {
            if (field.getName().equals(fieldName)) {
                return field;
            }
        }

        // 否则,查看父类中是否存在此字段,如果有则返回
        Class superClazz = clazz.getSuperclass();
        if (superClazz != null && superClazz != Object.class) {
            return getFieldByName(fieldName, superClazz);
        }

        // 如果本类和父类都没有,则返回空
        return null;
    }

    
    public static  LinkedList addCellInVertical(List data, Map dataTitle,
            Map cellFormatMap, int rowIndex, int colIndex) {
        Label label = new Label(1, 1, "中午");

        LinkedList cells = new LinkedList<>();
        cells.add(label);
        return cells;
    }

    

    /**
     * 
     * @param fileName
     * @param request
     * @param response
     */
    private static void setResp(HttpServletRequest request, HttpServletResponse response, String fileName) {
        final String userAgent = request.getHeader("USER-AGENT");

        response.reset();
        // response.setContentType("application/vnd.ms-excel"); // 改成输出excel文件  
        response.setContentType("application/x-msdownload");

        try {
            String finalFileName = null;

            if (StringUtils.contains(userAgent, "MSIE")) {// IE浏览器
                finalFileName = URLEncoder.encode(fileName, "UTF8");
                }

            else if (StringUtils.contains(userAgent, "Mozilla")) {// google,火狐浏览器
                finalFileName = new String(fileName.getBytes(), "ISO8859-1");
                }

            else {
                finalFileName = URLEncoder.encode(fileName, "UTF8");// 其他浏览器
            }

            String dataStr =  new SimpleDateFormat("yyyyMMddHHmmss").format(
                    new Date()).toString();

            // 这里设置一下让浏览器弹出下载提示框,而不是直接在浏览器中打开
            response.setHeader("Content-Disposition", "attachment; filename=" + finalFileName+"_"+dataStr + ".xls");
        } catch (UnsupportedEncodingException e) {
        }

    }
    
    /**
     * 设置工作表自动列宽和首行加粗
     *
     * @param ws
     *            要设置格式的工作表
     * @param extraWith
     *            额外的宽度
     */
    public static void setColumnAutoSize(WritableSheet ws, int extraWith) {
        // 获取本列的最宽单元格的宽度
        for (int i = 0; i < ws.getColumns(); i++) {
            int colWith = 0;
            for (int j = 0; j < ws.getRows(); j++) {
                String content = ws.getCell(i, j).getContents().toString();
                int cellWith = content.length();
                if (colWith < cellWith) {
                    colWith = cellWith;
                }
            }
            // 设置单元格的宽度为最宽宽度+额外宽度
            ws.setColumnView(i, colWith + extraWith);
        }

    }
    
//    public static NumberFormat getNumFormate(Object object){
//        NumberFormat numberFormat = new NumberFormat("");
//        
//    }
    
    public static void testExport(HttpServletRequest request,HttpServletResponse response) {
        try {
            setResp(request,response,"test");

            OutputStream outputStream = response.getOutputStream();

            WritableWorkbook wwb = Workbook.createWorkbook(outputStream);
            
            WritableSheet sheet = wwb.createSheet("test", 0);

            Label label = new Label(1, 1, "中午");
            sheet.addCell(label);

            wwb.write();
            wwb.close();

        } catch (WriteException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

 

邮件工具类

 

package com.yx.mail;


import java.io.ByteArrayInputStream;
import java.security.GeneralSecurityException;
import java.util.Properties;
import javax.activation.DataHandler;
import javax.activation.DataSource;
import javax.mail.Address;
import javax.mail.Authenticator;
import javax.mail.Message;
import javax.mail.Part;
import javax.mail.PasswordAuthentication;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.AddressException;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMultipart;
import javax.mail.internet.MimeUtility;
import javax.mail.internet.MimeMessage.RecipientType;
import javax.mail.util.ByteArrayDataSource;
import com.sun.mail.util.MailSSLSocketFactory;



public class BaseMail {

	/**
	 * 发送邮件
	 * 
	 * @param to
	 *            邮件收件人地址
	 * @param copyEmail
	 *            邮件抄送地址
	 * @param title
	 *            邮件标题
	 * @param text
	 *            内容
	 * @param fileDs
	 *            附件流
	 */
	public void sendMsgFileDs(String to, String copyEmail, String title, String text,ByteArrayInputStream inputstream) {
		Session session = assembleSession();
		Message msg=new MimeMessage(session);
		try {
			msg.setFrom(new InternetAddress(MailConfig.sendAddress));
			msg.setSubject(title);
			msg.setRecipients(RecipientType.TO, acceptAddressList(to,copyEmail));
			MimeBodyPart contentPart=(MimeBodyPart) createContent(text,inputstream);//参数为正文内容和附件流
//			MimeBodyPart stream = new MimeBodyPart();
			
//			MimeBodyPart part=(MimeBodyPart) createAttachment("D:/test/1.jpg");//可增加多个附件
			MimeMultipart mime=new MimeMultipart("mixed");
			mime.addBodyPart(contentPart);
//			mime.addBodyPart(part);//可增加多个附件
			msg.setContent(mime);
			Transport.send(msg);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	public Address[] acceptAddressList(String acceptAddress,String acceptAddressOther) {
		// 创建邮件的接收者地址,并设置到邮件消息中
		Address[] tos = null;
		String [] copyEmail= acceptAddressOther.split(",");
		try {
			if (copyEmail != null) {
				// 为每个邮件接收者创建一个地址
				tos = new InternetAddress[copyEmail.length + 1];
				tos[0] = new InternetAddress(acceptAddress);
				for (int i = 0; i < copyEmail.length; i++) {
					tos[i + 1] = new InternetAddress(copyEmail[i]);
				}
			} else {
				tos = new InternetAddress[1];
				tos[0] = new InternetAddress(acceptAddress);
			}
		} catch (AddressException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
        return tos;
	}
	
	public Session assembleSession() {
		Session session = null;
		Properties props=new Properties();
		props.setProperty("mail.smtp.auth", "true");
		props.setProperty("mail.transport.protocol", "smtp");
		props.setProperty("mail.smtp.port", MailConfig.mailSmtpPort);
		props.setProperty("mail.smtp.host", MailConfig.mailSmtpHost);//邮件服务器
		//开启安全协议
        MailSSLSocketFactory sf = null;
        try {
            sf = new MailSSLSocketFactory();
            sf.setTrustAllHosts(true);
        } catch (GeneralSecurityException e1) {
            e1.printStackTrace();
        }
		props.put("mail.smtp.ssl.socketFactory", sf);
		props.put("mail.smtp.ssl.enable", "true");
		session = Session.getDefaultInstance(props, new MyAuthenricator(MailConfig.sendAddress,MailConfig.sendPassWord));
		return session;
	}
	
	static Part createContent(String content,ByteArrayInputStream inputstream){
		MimeBodyPart contentPart=null;
		try {
			contentPart=new MimeBodyPart();
			MimeMultipart contentMultipart=new MimeMultipart("related");
			MimeBodyPart htmlPart=new MimeBodyPart();
			htmlPart.setContent(content, "text/html;charset=gbk");
			contentMultipart.addBodyPart(htmlPart);
			//附件部分
			MimeBodyPart excelBodyPart=new MimeBodyPart();
			DataSource dataSource=new ByteArrayDataSource(inputstream, "application/excel");
            DataHandler dataHandler=new DataHandler(dataSource);
            excelBodyPart.setDataHandler(dataHandler);
            excelBodyPart.setFileName(MimeUtility.encodeText("通知.xls"));
//			excelBodyPart.setDataHandler(new DataHandler(fileDs));
//			excelBodyPart.setFileName(fileDs.getName());
//			excelBodyPart.setContentID("excel");
			contentMultipart.addBodyPart(excelBodyPart);
			contentPart.setContent(contentMultipart);
		} catch (Exception e) {
			e.printStackTrace();
		}
		return contentPart;
	}
	
	//用户名密码验证,需要实现抽象类Authenticator的抽象方法PasswordAuthentication
    static class MyAuthenricator extends Authenticator{  
        String u = null;  
        String p = null;  
        public MyAuthenricator(String u,String p){  
            this.u=u;  
            this.p=p;  
        }  
        @Override  
        protected PasswordAuthentication getPasswordAuthentication() {  
            return new PasswordAuthentication(u,p);  
        }  
    }
    
}

 

测试类:

package com.yx.mail;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;


@Controller
@Scope(value = "prototype")
public class TestMain extends BaseController{

	private Logger logger = LogManager.getLogger(TestMain.class);
	@Autowired 
	private SendEmailService sendEmailService;
	@RequestMapping(value = "/mail", method = RequestMethod.GET)
	@ResponseBody
	public String sendEmail() {
		
		List rechecklist = new ArrayList();
		Order Order1 = new Order();
		Order1.setOrderNo("1");
		Order1.setProvinceId("黑龙江");
		Order Order2 = new Order();
		Order2.setOrderNo("2");
		Order2.setProvinceId("吉林");
		Order Order3 = new Order();
		Order3.setOrderNo("3");
		Order3.setProvinceId("辽宁");
		Order Order4 = new Order();
		Order4.setOrderNo("4");
		Order4.setProvinceId("福建");
		rechecklist.add(Order1);rechecklist.add(Order2);rechecklist.add(Order3);rechecklist.add(Order4);
		
		
		
		LinkedHashMap recheckMap = new LinkedHashMap<>();
		recheckMap.put("provinceId", "省份");
		recheckMap.put("orderNo", "订单号");
		
		try {
			ByteArrayOutputStream baos = JxlUtil.listToExcelReturnOut(rechecklist, recheckMap, "test", "testexport", 0, 0, request,
					response);
			ByteArrayInputStream InputStream = new ByteArrayInputStream(baos.toByteArray());
			String email = MailConfig.acceptAddress;//接收人
			String copyEmail = MailConfig.acceptAddressOther;//抄送多人以逗号分隔
			String title = "XX通知";
			String context = "当前系统存在"+rechecklist.size()+"条需要处理的订单。详情请留意附件。";
			sendEmailService.sendMsgFileDs(email,copyEmail,title,context,InputStream);
//			file.deleteOnExit();
		} catch (Exception e) {
			logger.info(e.getMessage());
			logger.info("【MailNoticeController】sendEmail 异常");
		}
		
		return null;
	}
}

 

你可能感兴趣的:(eclipse,java,excel附件,不生成文件,邮件)