Spring MVC 实现Excel的导入导出功能(2:Excel的导入优化和Excel的导出)

Excel的导入V2优化版

有些时候文件上传这一步骤由前端来处理,只将上传后的 URL 传输给后端(可以参考上一文中的图片上传功能),也就是导入请求中并不会直接处理 MultipartFile 对象,而是通过文件 URL 将其下载,之后进行文件流处理,具体过程如下:

 

点击 “ 导入V2 ” 按钮,与 V1 按钮不同,此时出现的不是文件选择框,而是一个上传文件的弹框,点击弹框里的 “ 上传 Excel 文件 ” 按钮才会出现文件选择框,选择正确的文件后点击确定,请求完成后同 V1 版本的导入功能效果一样。

前端实现

HTML页面

<!-- Main content --><div class="content"><div class="row"><div class="col-12"><div class="card"><div class="card-body"><div class="grid-btn"><button class="btn btn-info" onclick="userAdd()"><i class="fa fa-plus"></i>&nbsp;新增</button><button class="btn btn-success" onclick="userEdit()"><i class="fa fa-plus"></i>&nbsp;编辑</button><button class="btn btn-danger" onclick="deleteUser()"><i class="fa fa-remove"></i>&nbsp;删除</button><button class="btn btn-default" id="importV1Button"><i class="fa fa-upload"></i>&nbsp;导入ExcelV1</button><button class="btn btn-default" onclick="importV2()"><iclass="fa fa-upload"></i>&nbsp;导入ExcelV2</button><button class="btn btn-primary" onclick="window.location.href='/users/export'"><i class="fa fa-download"></i> 导出</button></div><table id="jqGrid" class="table table-bordered"></table><div id="jqGridPager"></div></div></div></div></div></div>
</div>

HTML模态框(这里用的是Custombox)

<!--导入Excel模态框--><div class="content"><div class="modal" id="importV2Modal" tabindex="-1" role="dialog" aria-labelledby="importV2ModalLabel"><div class="modal-dialog" role="document"><div class="modal-content"><div class="modal-header"><h6 class="modal-title" id="importV2ModalLabel">用户导入</h6></div><div class="modal-body"><div class="form-group"><input type="hidden" id="fileUrl" value=""><div class="col-sm-10"><a class="btn btn-default" id="uploadExcelV2"><i class="fa fa-file">上传Excel文件</i></a></div></div></div><div class="modal-footer"><button type="button" class="btn btn-default" id="cancelImportV2">取消</button><button type="button" class="btn btn-primary" id="importV2Button">确认</button></div></div></div></div></div>

导入的JS

// 用户导入V2
function importV2() {var modal = new Custombox.modal({content: {effect: 'fadein',target: '#importV2Modal'}});modal.open();
}
    new AjaxUpload('#uploadExcelV2', {action: 'upload/file',name: 'file',autoSubmit: 'true',responseType: 'json',onSubmit: function (file, extension) {if (!(extension && /^(xlsx)$/.test(extension.toLowerCase()))) {alert('只支持xlsx格式的文件!', {icon: "error",});return false;}},onComplete: function (file, r) {if (r.resultCode == 200) {$("#fileUrl").val(r.data);$("#uploadExcelV2").attr('class','btn-info');alert("上传Excel成功,请点击确认添加数据");return false;} else {alert(r.message);}}});
});

这里还是通过上传获取文件的url再通过流处理转换成File类型并把对应的Excel下载到服务器,其他的和V1版本是一样的

当点击确定导入的时候,如果之前上传没错,再访问一次后台

$("#importV2Button").click(function () {var fileUrl = $("#fileUrl").val();$.ajax({type: 'post',dataType: 'json',url: 'users/importV2?fileUrl=' + fileUrl,contentType:'application/json',success:function (result) {if (result.resultCode==200){closeModal();reload();alert("成功导入"+result.data+"条记录!");}else {closeModal();alert(result.message);};},error:function () {reset();alert("操作失败!");}});
});

后端逻辑

控制层

package com.ssm.demo.controller;import com.ssm.demo.common.Result;
import com.ssm.demo.common.ResultGenerator;
import com.ssm.demo.controller.enums.UploadFileTypeEnum;
import com.ssm.demo.utils.FileUtil;
import org.apache.commons.io.FileUtils;
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.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Random;import static com.ssm.demo.common.Constants.FILE_PRE_URL;/*** Created by 13 on 2017/7/17.*/
@Controller
@RequestMapping("/upload")
public class UploadFileController {/*** 通用 文件上传接口(可以上传图片、视频、excel等文件,具体格式可在UploadFileTypeEnum中进行配置)** @return*/@RequestMapping(value = "/file", method = RequestMethod.POST)@ResponseBodypublic Result uploadFile(HttpServletRequest request, @RequestParam("file") MultipartFile file) {ServletContext sc = request.getSession().getServletContext();String type = file.getOriginalFilename().substring(file.getOriginalFilename().lastIndexOf(".") + 1, file.getOriginalFilename().length());String fileName = null;UploadFileTypeEnum uploadFileTypeEnum = UploadFileTypeEnum.getFileEnumByType(type);if (uploadFileTypeEnum == UploadFileTypeEnum.ERROR_TYPE) {//格式错误则不允许上传,直接返回错误提示return ResultGenerator.genFailResult("请检查文件格式!");} else {//生成文件名称通用方法SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd_HHmmss");Random r = new Random();StringBuilder tempName = new StringBuilder();tempName.append(sdf.format(new Date())).append(r.nextInt(100)).append(".").append(type);fileName = tempName.toString();}try {String dir = sc.getRealPath("/upload");FileUtils.writeByteArrayToFile(new File(dir, fileName), file.getBytes());} catch (IOException e) {//文件上传异常return ResultGenerator.genFailResult("文件上传失败!");}Result result = ResultGenerator.genSuccessResult();//返回文件的全路径StringBuilder fileUrl = new StringBuilder();fileUrl.append(FILE_PRE_URL).append("/upload/").append(fileName);result.setData(fileUrl.toString());return result;}/*** @param chunks 当前所传文件的分片总数* @param chunk  当前所传文件的当前分片数* @return* @Description: 大文件上传前分片检查* @author: 13*/@ResponseBody@RequestMapping(value = "/checkChunk")public Result checkChunk(HttpServletRequest request, String guid, Integer chunks, Integer chunk, String fileName) {try {String uploadDir = FileUtil.getRealPath(request);String ext = fileName.substring(fileName.lastIndexOf("."));// 判断文件是否分块if (chunks != null && chunk != null) {//文件路径StringBuilder tempFileName = new StringBuilder();tempFileName.append(uploadDir).append(File.separator).append("temp").append(File.separator).append(guid).append(File.separator).append(chunk).append(ext);File tempFile = new File(tempFileName.toString());//是否已存在分片,如果已存在分片则返回SUCCESS结果if (tempFile.exists()) {return ResultGenerator.genSuccessResult("分片已经存在!跳过此分片!");}}} catch (Exception ex) {ex.printStackTrace();return ResultGenerator.genFailResult("error");}return ResultGenerator.genNullResult("不存在分片");}/*** @param chunks 当前所传文件的分片总数* @param chunk  当前所传文件的当前分片数* @return* @Description: 大文件分片上传* @author: 13*/@ResponseBody@RequestMapping(value = "/files")public Result upload(HttpServletRequest request, String guid, Integer chunks, Integer chunk, String name, MultipartFile file) {String filePath = null;//上传存储路径String uploadDir = FileUtil.getRealPath(request);//后缀名String ext = name.substring(name.lastIndexOf("."));StringBuilder tempFileName = new StringBuilder();//等价于 uploadDir + "\\temp\\" + guid + "\\" + chunk + exttempFileName.append(uploadDir).append(File.separator).append("temp").append(File.separator).append(guid).append(File.separator).append(chunk).append(ext);File tempFile = new File(tempFileName.toString());// 判断文件是否分块if (chunks != null && chunk != null) {//根据guid 创建一个临时的文件夹if (!tempFile.exists()) {tempFile.mkdirs();}try {//保存每一个分片
                file.transferTo(tempFile);} catch (Exception e) {e.printStackTrace();}//如果当前是最后一个分片,则合并所有文件if (chunk == (chunks - 1)) {StringBuilder tempFileFolder = new StringBuilder();//等价于 uploadDir + "\\temp\\" + guid + File.separatortempFileFolder.append(uploadDir).append(File.separator).append("temp").append(File.separator).append(guid).append(File.separator);String newFileName = FileUtil.mergeFile(chunks, ext, tempFileFolder.toString(), request);filePath = "upload/chunked/" + newFileName;}} else {//不用分片的文件存储到files文件夹中StringBuilder destPath = new StringBuilder();destPath.append(uploadDir).append(File.separator).append("files").append(File.separator);String newName = System.currentTimeMillis() + ext;// 文件新名称try {FileUtil.saveFile(destPath.toString(), newName, file);} catch (IOException e) {e.printStackTrace();}filePath = "upload/files/" + newName;}Result result = ResultGenerator.genSuccessResult();result.setData(filePath);return result;}
}

FileUtil工具类

package com.ssm.demo.utils;import org.apache.commons.io.FileUtils;
import org.springframework.web.multipart.MultipartFile;import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import java.io.*;
import java.net.URL;
import java.net.URLConnection;
import java.util.UUID;public class FileUtil {/*** 转换MultipartFile对象为java.io.File类型** @param multipartFile* @return*/public static File convertMultipartFileToFile(MultipartFile multipartFile) {File result = null;try {/*** UUID.randomUUID().toString()是javaJDK提供的一个自动生成主键的方法。* UUID(Universally Unique Identifier)全局唯一标识符,是指在一台机器上生成的数字,* 它保证对在同一时空中的所有机器都是唯一的,是由一个十六位的数字组成,表现出来的形式。* 由以下几部分的组合:当前日期和时间(UUID的第一个部分与时间有关,如果你在生成一个UUID之后,* 过几秒又生成一个UUID,则第一个部分不同,其余相同),时钟序列,* 全局唯一的IEEE机器识别号(如果有网卡,从网卡获得,没有网卡以其他方式获得),* UUID的唯一缺陷在于生成的结果串会比较长。*** File.createTempFile和File.createNewFile()的区别:*  后者只是创建文件,而前者可以给文件名加前缀和后缀*///这里对生成的文件名加了UUID随机生成的前缀,后缀是nullresult = File.createTempFile(UUID.randomUUID().toString(), null);multipartFile.transferTo(result);result.deleteOnExit();} catch (Exception e) {e.printStackTrace();}return result;}/*** 根据url获取文件对象** @param fileUrl* @return*/public static File downloadFile(String fileUrl) {File result = null;try {result = File.createTempFile(UUID.randomUUID().toString(), null);URL url = new URL(fileUrl);URLConnection connection = url.openConnection();connection.setConnectTimeout(3000);BufferedInputStream bis = new BufferedInputStream(connection.getInputStream());BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(result));byte[] car = new byte[1024];int l = 0;while ((l = bis.read(car)) != -1) {bos.write(car, 0, l);}bis.close();bos.close();} catch (Exception e) {e.printStackTrace();}return result;}/*** @param request* @return*/public static String getRealPath(HttpServletRequest request) {ServletContext sc = request.getSession().getServletContext();String uploadDir = sc.getRealPath("/upload");return uploadDir;}public static boolean saveFile(String savePath, String fileFullName, MultipartFile file) throws IOException {File uploadFile = new File(savePath + fileFullName);FileUtils.writeByteArrayToFile(new File(savePath, fileFullName), file.getBytes());return uploadFile.exists();}public static String mergeFile(int chunksNumber, String ext, String uploadFolderPath,HttpServletRequest request) {//合并分片流String mergePath = uploadFolderPath;String destPath = getRealPath(request);// 文件路径String newName = System.currentTimeMillis() + ext;// 文件新名称
        SequenceInputStream s;InputStream s1;try {s1 = new FileInputStream(mergePath + 0 + ext);String tempFilePath;InputStream s2 = new FileInputStream(mergePath + 1 + ext);s = new SequenceInputStream(s1, s2);for (int i = 2; i < chunksNumber; i++) {tempFilePath = mergePath + i + ext;InputStream s3 = new FileInputStream(tempFilePath);s = new SequenceInputStream(s, s3);}//分片文件存储到/upload/chunked目录下StringBuilder filePath = new StringBuilder();filePath.append(destPath).append(File.separator).append("chunked").append(File.separator);saveStreamToFile(s, filePath.toString(), newName);// 删除保存分块文件的文件夹
            deleteFolder(mergePath);} catch (FileNotFoundException e) {e.printStackTrace();} catch (Exception e) {e.printStackTrace();}return newName;}private static boolean deleteFolder(String mergePath) {File dir = new File(mergePath);File[] files = dir.listFiles();if (files != null) {for (File file : files) {try {file.delete();} catch (Exception e) {e.printStackTrace();}}}return dir.delete();}private static void saveStreamToFile(SequenceInputStream inputStream, String filePath, String newName)throws Exception {File fileDirectory = new File(filePath);synchronized (fileDirectory) {if (!fileDirectory.exists()) {if (!fileDirectory.mkdir()) {throw new Exception("文件夹创建失败,路径为:" + fileDirectory);}}if (!fileDirectory.exists()) {if (!fileDirectory.mkdir()) {throw new Exception("文件夹创建失败,路径为:" + fileDirectory);}}}OutputStream outputStream = new FileOutputStream(filePath + newName);byte[] buffer = new byte[1024];int len = 0;try {while ((len = inputStream.read(buffer)) != -1) {outputStream.write(buffer, 0, len);outputStream.flush();}} catch (Exception e) {e.printStackTrace();throw e;} finally {outputStream.close();inputStream.close();}}
}

其他的和V1版本是一样的

mapper

  <update id="deleteUser">update tb_admin_user set is_deleted = 1where id in<foreach collection="array" item="id" open="(" separator="," close=")">#{id}</foreach></update>

效果展示

Excel的导出实现

前端实现

    <button class="btn btn-primary"     onclick="window.location.href='/users/export'"><i class="fa fa-download"></i> 导出</button>

点击的时候会直接访问到Controller层来获取下载

后端逻辑

 

/*** 导出功能*/@RequestMapping(value = "/export", method = RequestMethod.GET)public void exportUsers(HttpServletRequest request, HttpServletResponse response) {List<AdminUser> userList = adminUserService.getUsersForExport();//单元格表头String[] excelHeader = {"用户id", "用户名", "账号状态", "添加时间"};//字段名称String[] fileds = {"userId", "userName", "status", "createTime"};//单元格宽度内容格式int[] formats = {4, 2, 1, 1};//单元格宽度int[] widths = {256 * 14, 512 * 14, 256 * 14, 512 * 14};try {List<Map<String, Object>> excelData = new ArrayList<Map<String, Object>>();SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");if (CollectionUtils.isNotEmpty(userList)) {for (AdminUser user : userList) {Map<String, Object> map = new HashMap<>();map.put("userId", user.getId());map.put("userName", user.getUserName());map.put("status", user.getIsDeleted() == 0 ? "正常账号" : "废弃账号");map.put("createTime", formatter.format(user.getCreateTime()));excelData.add(map);}}String excelName = "用户数据_" + System.currentTimeMillis();PoiUtil.exportFile(excelName, excelHeader, fileds, formats, widths, excelData, request, response);} catch (Exception e) {e.printStackTrace();}}

 

注:这里的一些工具类在上一篇写过了

 

转载于:https://www.cnblogs.com/xiaowangtongxue/p/10747393.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/536521.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

计算机系统应用属于ei,2018年度中心成员发表论文清单(SCI、EI收录)

序号论文名称作者发表时间发表刊物名及ISSN号论文类别(SCI(请写明几区)、EI、AB类刊物、CSCD)1Power Allocation Study for Non-Orthogonal Multiple Access Networks With Multicast-Unicast TransmissionZheng Yang , Member, IEEE, Jamal Ahmed Hussein , Peng Xu , Member,…

ts基础总结

ts有什么用 类型检查, 拥抱es6&#xff0c;支持部分的esNext草案&#xff0c;直接编译到原生js、引入新的语法糖 为什么用ts TypeScript的设计目的应该是解决JavaScript的“痛点”&#xff1a;弱类型和没有命名空间&#xff0c;导致很难模块化&#xff0c;不适合开发大型程序。…

CentOS7启用SELinux和Firewall修改ssh端口号

基本信息 CentOS :CentOS Linux release 7.6.1810 (Core)SELinux:enforcedFirewall:enforcing生成ssh密钥对 生成密钥对 ssh-keygen -t rsa -b 2048 #默认存放的位置是/home/user/.ssh&#xff0c;使用的是公钥id_rsa.pub从服务器中将私钥复制到本机或者使用本地生成的密钥对&a…

华为智能手环智能手表软件测试,一块智能手表的测试之旅,揭秘华为运动健康科学实验室...

随着消费者对健康生活的日益关注&#xff0c;随之而来的是智能可穿戴设备的蓬勃发展。一个手环&#xff0c;一个智能手表&#xff0c;都可以为消费者提供诸如心率&#xff0c;步数相关的数据统计。而更进阶的设备&#xff0c;则能为用户提供系统的运动解决方案以及监控人体健康…

Flutter 使用动画播放一组图片

请支持原文&#xff1a;tryenough.com/images-anim… 效果如下图&#xff1a; 代码 import package:flutter/material.dart; import package:sprintf/sprintf.dart; //这个是一个拼接字符串的flutter库&#xff0c;主要是为了使用方便&#xff0c;你可以选择不使用&#xff0c…

软件测试都有哪些证书,软件测试都有哪些证书呀?有用吗?

OYWP学习论坛 www.oywp.netOYWP学习论坛 www.oywp.netOYWP学习论坛 www.oywp.netOYWP学习论坛 www.oywp.netOYWP学习论坛 www.oywp.netOYWP学习论坛 www.oywp.netOYWP学习论坛 www.oywp.netOYWP学习论坛 www.oywp.netOYWP学习论坛 www.oywp.netOYWP学习论坛 www.oywp.netOYWP学…

超低频测试信号产生电路软件流程图,一种0_20Hz超低频信号发生器的设计与实现...

。。第22卷第4期增刊           仪 器 仪 表 学 报             2001年8月一种0&#xff5e;20Hz超低频信号发生器的设计与实现马彦恒 郭 利 于久恩 (军械工程学院 石家庄 050003)摘要 本文介绍了一种采用了主—从式双CPU结构,频率和幅度都…

datastage 使用小结

转载于:https://www.cnblogs.com/Guhan-xue/p/10758663.html

Teams 的逻辑架构与语音解决方案 - Official Posters Download

意外收获了前几天&#xff08;0420&#xff09;刚刚更新出来的Teams架构海报&#xff0c;分享给大家下载 Microsoft Teams IT architecture and telephony solutions postershttps://docs.microsoft.com/en-us/microsoftteams/teams-architecture-solutions-posters 接下来跟大…

ug不能自动启动服务器,没有足够的权限启动系统服务解决方法

没有足够的权限启动系统服务解决方法UG的安装要注意两个问题&#xff0c;一个是安装路径不能有中文(包括ug文件存储路径也不能含有中文),二是许可证的安装&#xff0c;win7装ug问题多数出在“许可证”的安装过程&#xff0c;你的那个报警是&#xff1a;就是说你把UGII_TMP_DIR设…

css直接子元素怎么用,CSS 子元素选择器使用实例

与后代选择器相比&#xff0c;子元素选择器只能选择作为某元素子元素的元素。子元素选择器使用大于号">"做为连接符。如果您不希望选择任意的后代元素&#xff0c;而是希望缩小范围&#xff0c;只选择某个元素的子元素&#xff0c;请使用子元素选择器子元素选择器…

C# Collection was modified;enumeration operation may not execute

一、问题描述 在做 数组、列表、集合遍历时&#xff0c;可能我们会遇见这个问题。Collection was modified;enumeration operation may not execute &#xff0c;翻译的中文意思&#xff1a;集合已修改&#xff1b;枚举操作可能无法执行。 二、解决方案 就是在遍历时&#xff0…

xp系统安装金蝶k3服务器配置,XP单机版安装金蝶K3的13.1版本号,金蝶K3Wise安装步骤,安装成功...

要注意先安装IIS。再安装VS2008。我们会常常在控制面板里找不到“应用程序server”这个项目。我们须要依照以下的步骤来操作就会Ok。1.下载IIS6&#xff0c;放置到D盘根文件夹。2.在执行里输入&#xff1a;c:\Windows\inf\sysoc.inf即打开找到 [Components]段。加入&#xff1a…

通过django 执行命令或者脚本

1、执行django命令 下文的project1为project名&#xff0c;app1为app名 在django project 下执行&#xff1a;python manage.py shell # 可以在Python console里执行Python命令或者包含django的命令 > from app1.models import User > user User.objects.get(name"…

服务器kvm切换器维修,KVM切换器常见问题

数据中心在使用kvm开关的过程中有时会遇到一些使用问题。如果他们不能及时解决问题&#xff0c;就会影响数据中心的有效管理。小编辑总结了一些常见的问题并给出了详细的答案。我希望能帮助每一个人。经常问的问题和答案&#xff1a;i.用于初始连接的kvm转换器&#xff0c;kvm开…

Zulip 2.0.3 发布,功能强大的群组聊天软件

Zulip 2.0.3 发布了&#xff0c;Zulip 是一个强大的开源群组聊天软件。 用 Python 编写&#xff0c;使用 Django 框架&#xff0c;支持通过会话流的私人消息和群聊。Zulip 还支持快速搜索、拖放文件上传、图像预览、组私人消息、可听通知、错过电子邮件消息提醒&#xff0c;桌面…

ifix怎么装服务器系统上,ifix服务器和客户端配置

ifix服务器和客户端配置 内容精选换一换准备好服务端和客户端&#xff0c;根据组网规划完成物理服务器的物理组网。本文档中以3台客户端和3台TaiShan服务器作为服务端为例。本次部署流程中以3台客户端节点和3台TaiShan 200 服务器(型号2280)作为存储节点&#xff0c;网络包含前…

接口测试工具Postman(转)

接口测试工具Postman Postman是一款功能强大的HTTP调试与模拟插件&#xff0c;不仅可以调试简单的CSS、HTML、脚本等网页的基本信息&#xff0c;它还可以发送几乎所有类型的HTTP请求。Postman适用于不同的操作系统&#xff0c;Mac、WindowsX32、Windows X64、Linux系统等。本篇…

洛谷 P1372 又是毕业季I

可能所有的数论题都是这样玄学.... 题目链接&#xff1a;https://www.luogu.org/problemnew/show/P1372 这道题通过暴力的枚举可以发现是不可做的&#xff08;当然我也不会做&#xff09; 然后就有了这样一个思路&#xff1a; 这道题就是求&#xff1a;从1~n中取k个数&#xff…

查看游戏服务器ip地址网站,如何查看游戏服务器IP地址

如何尽量使网络游戏避免卡机呢&#xff1f;高带宽&#xff0c;短延时&#xff0c;丢包率这是大家都知道的&#xff0c;还有重要的一点大家容易忽视&#xff0c;卡机与选择的游戏分区有着密切的联系&#xff0c;选择合适的游戏服务器(南北电信运营商之分)最为重要。1、先选择一个…