1.什么是jquery-file-upload
jQuery File Upload是一个非常优秀的上传组件,主要使用了XHR作为上传方式,并且利用了相当多的现代浏览器功能,所以可以实现诸如批量上传、超大文件上传、图片预览、拖拽上传、上传进度显示、跨域上传等功能。
美中不足的是jQuery File Upload的默认UI比较复杂,集成了全部功能,让jQuery File Upload的定制变得比较繁琐。同时jquery-file-upload官网上对angualrjs集成没有相关的API,doc 使得jquery-file-upload在angualrjs中难度极为复杂,网上关于相关集成的文章少之又少,故在此介绍。
2.jquery-file-upload 与angualrJs 集成效果图
影集预览自动播放功能
3.相关实现
1.需要的js库
query-file-upload 方面
预览效果依赖blueimp 的 gallery库 相关资源如下:
为了预览效果的实现还需要load-image.all.min.js和canvas-to-blob.min.js这两个js 可以从网上下载
相关资源整合: http://pan.baidu.com/s/1slN6Jox
2.在app中引入相关js css
在app中引入相关module
3.html的编写
<div > <h1>文件上传</h1> <!-- The file upload form used as target for the file upload widget --> <div class="panel panel-default"> <div class="panel-heading"> <h3 class="panel-title">注意事项</h3> </div> <div class="panel-body"> <ul> <li>单个图片上传大小限制为<strong>999 KB</strong> (default file size is unlimited).</li> <li>前选择对应格式的图片上传 (<strong>JPG, GIF, PNG</strong>)</li> </ul> </div> </div> <form id="fileupload" method="POST" data-ng-controller="uploadController"; enctype="multipart/form-data" data-file-upload="options" data-ng-class="{'fileupload-processing': processing() || loadingFiles}"> <!-- The fileupload-buttonbar contains buttons to add/delete files and start/cancel the upload --> <div class="row fileupload-buttonbar"> <div class="col-lg-5"> <!-- The fileinput-button span is used to style the file input field as button --> <span class="btn btn-success fileinput-button" ng-class="{disabled: disabled}"> <i class="glyphicon glyphicon-plus"></i> <span>添加附件</span> <input type="file" name="files[]" ng-show="options.maxNumberOfFiles==1" ng-disabled="disabled"> <input type="file" name="files[]" multiple ng-show="!options.maxNumberOfFiles" ng-disabled="disabled"> </span> <button type="button" class="btn btn-primary start" data-ng-click="submit()"> <i class="glyphicon glyphicon-upload"></i> <span>全部开始</span> </button> <button type="button" class="btn btn-danger cancel" data-ng-click="destoryAll()"> <i class="glyphicon glyphicon glyphicon-trash"></i> <span>全部删除</span> </button> <!-- The global file processing state --> <span class="fileupload-process"></span> </div> <!-- The global progress state --> <div class="col-lg-4 fade" data-ng-class="{in: active()}"> <!-- The global progress bar --> <div class="progress progress-striped active" data-file-upload-progress="progress()"><div class="progress-bar progress-bar-success" data-ng-style="{width: num + '%'}"></div></div> <!-- The extended global progress state --> <div class="progress-extended"> </div> </div> <div class="col-lg-3"> <button type="button" class="btn btn-warning" ng-click="cancelUpload()" style="float:right"> <i class="glyphicon glyphicon-ban-circle"></i> <span>取消</span> </button> <button type="button" class="btn btn-info" ng-click="saveUpload()" ng-disabled="saveStatus" style="float:right;margin-right:5px"> <i class="glyphicon glyphicon-save"></i> <span>保存</span> </button> </div> </div> <!-- The table listing the files available for upload/download --> <table class="table table-striped files ng-cloak"> <tr data-ng-repeat="file in queue" data-ng-class="{'processing': file.$processing()}"> <td data-ng-switch data-on="!!file.thumbnailUrl"> <div class="preview" data-ng-switch-when="true"> <a data-ng-href="{{file.url}}" title="{{file.name}}" download="{{file.name}}" data-gallery> <img data-ng-src="{{file.thumbnailUrl}}" alt="" width="80" height="80"> </a> </div> <div class="preview" data-ng-switch-default data-file-upload-preview="file"></div> </td> <td> <p class="name" data-ng-switch data-on="!!file.url"> <span data-ng-switch-when="true" data-ng-switch data-on="!!file.thumbnailUrl"> <a data-ng-switch-when="true" data-ng-href="{{file.url}}" title="{{file.name}}" download="{{file.name}}" data-gallery>{{file.name}}</a> <a data-ng-switch-default data-ng-href="{{file.url}}" title="{{file.name}}" download="{{file.name}}">{{file.name}}</a> </span> <span data-ng-switch-default>{{file.name}}</span> </p> <strong data-ng-show="file.error" class="error text-danger">{{file.error}}</strong> </td> <td> <p class="size">{{file.size | formatFileSize}}</p> <div class="progress progress-striped active fade" data-ng-class="{pending: 'in'}[file.$state()]" data-file-upload-progress="file.$progress()"><div class="progress-bar progress-bar-success" data-ng-style="{width: num + '%'}"></div></div> </td> <td style="text-align:right"> <button type="button" class="btn btn-primary start" data-ng-click="file.$submit()" data-ng-hide="!file.$submit || options.autoUpload || file.status" data-ng-disabled="file.$state() == 'pending' || file.$state() == 'rejected'"> <i class="glyphicon glyphicon-upload"></i> <span>开始</span> </button> <button type="button" class="btn btn-warning cancel" data-ng-click="file.$cancel()" data-ng-hide="!file.$cancel || file.status"> <i class="glyphicon glyphicon-ban-circle"></i> <span>取消</span> </button> <button type="button" class="btn btn-danger destroy" ng-click="destory(file)" data-ng-hide="!file.status"> <i class="glyphicon glyphicon-trash"></i> <span>删除</span> </button> </td> </tr> </table> </form> <br> </div> <!-- The blueimp Gallery widget --> <div id="blueimp-gallery" class="blueimp-gallery blueimp-gallery-controls" data-filter=":even"> <div class="slides"></div> <h3 class="title"></h3> <a class="prev">‹</a> <a class="next">›</a> <a class="close">×</a> <a class="play-pause"></a> <ol class="indicator"></ol> </div>
var page=angular.module('upload.view',[]); page.config(['$httpProvider', 'fileUploadProvider', function ($httpProvider, fileUploadProvider) { delete $httpProvider.defaults.headers.common['X-Requested-With']; fileUploadProvider.defaults.redirect = window.location.href.replace( /\/[^\/]*$/, '/cors/result.html?%s' ); angular.extend(fileUploadProvider.defaults, { disableImageResize: /Android(?!.*Chrome)|Opera/.test(window.navigator.userAgent), maxFileSize: 999000, acceptFileTypes: /(\.|\/)(gif|jpe?g|png)$/i, maxNumberOfFiles :10 }); } ]); page.controller('uploadController', [ '$scope', '$rootScope', '$http', 'RoleService', '$state', '$stateParams', '$window', function($scope, $rootScope, $http, RoleService, $state, $stateParams, $window) { var url = '/servlet/UploadServlet'; $scope.options = { url : url, dataType : 'json', autoUpload : false, acceptFileTypes : /(\.|\/)(gif|jpe?g|png)$/i, previewCrop : true, maxNumberOfFiles :1 }; $scope.queue=[]; $scope.fileList=[]; $scope.saveStatus=true; $scope.$on('fileuploadadd', function(event, data) { if($scope.options.maxNumberOfFiles==1){ $scope.queue=[]; } }); $scope.$on('fileuploaddone', function(event, data) { if(data.result.length==1){ var uploadSuccessName=data.result[0].name; var uploadSuccessFileName=data.result[0].fileName; var uploadSuccessStatus=data.result[0].status; var uploadSuccessFileId=data.result[0].fileId; for(var i=0;i<$scope.queue.length;i++){ if($scope.queue[i].name==uploadSuccessName){ $scope.queue[i].url="/servlet/DownLoadServlet?f="+uploadSuccessFileName; $scope.queue[i].thumbnailUrl="/servlet/DownLoadServlet?f="+uploadSuccessFileName; $scope.queue[i].status=uploadSuccessStatus; $scope.queue[i].fileId=uploadSuccessFileId; $scope.saveStatus=false; } } } for(var i=0;i<$scope.queue.length;i++){ if($scope.queue[i].status==1){ $scope.saveStatus=false; }else{ $scope.saveStatus=true; break; } } }); $scope.destoryAll=function(){ $scope.clear($scope.queue); } $scope.saveUpload=function(){ $scope.fileList=[]; for(var i=0;i<$scope.queue.length;i++){ if($scope.queue[i].status){ $scope.fileList.push($scope.queue[i]); } } $scope.closeThisDialog($scope.fileList); } $scope.cancelUpload=function(){ $scope.closeThisDialog(); } $scope.destory=function(file){ $scope.clear(file); } }]);
UploadServlet
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException{ // 得到上传文件的保存目录,将上传的文件存放于WEB-INF目录下,不允许外界直接访问,保证上传文件的安全 String savePath = this.getServletContext().getRealPath("/WEB-INF/upload"); File file = new File(savePath); // 判断上传文件的保存目录是否存在 if (!file.exists() && !file.isDirectory()) { System.out.println(savePath + "目录不存在,需要创建"); // 创建目录 file.mkdirs(); } List<FileMeta> files = new LinkedList<FileMeta>(); // 1. 使用Apache的FileUpload上传文件 files.addAll(MultipartRequestHandler.getInstance().uploadByApacheFileUpload(request, savePath)); // Remove some files while (files.size() > 20) { files.remove(0); } // 2.设置响应类型的JSON response.setContentType("application/json"); // 3. 转换 List<FileMeta> 为 JSON 格式 ObjectMapper mapper = new ObjectMapper(); // 4. 发送结果到客户端 mapper.writeValue(response.getOutputStream(), files); }MultipartRequestHandler
public List<FileMeta> uploadByApacheFileUpload(HttpServletRequest request,String savePath) throws IOException, ServletException{ List<FileMeta> files = new LinkedList<FileMeta>(); // 1. 检查要求有多文件内容 boolean isMultipart = ServletFileUpload.isMultipartContent(request); FileMeta temp = null; // 2. If yes (it has multipart "files") if(isMultipart){ // 2.1 Apache的FileUpload类实例化 DiskFileItemFactory factory = new DiskFileItemFactory(); factory.setSizeThreshold(1024*100);//设置缓冲区的大小为100KB,如果不指定,那么缓冲区的大小默认是10KB ServletFileUpload upload = new ServletFileUpload(factory); upload.setHeaderEncoding("UTF-8"); //设置上传单个文件的大小的最大值,目前是设置为1024*1024*5字节,也就是5MB upload.setFileSizeMax(1024*1024*5); //设置上传文件总量的最大值,最大值=同时上传的多个文件的大小的最大值的和,目前设置为100MB upload.setSizeMax(1024*1024*100); // 2.2 解析请求 try { // 2.3 得到所有上传FileItem List<FileItem> items = upload.parseRequest(request); // 2.4 遍历每一个FileItem for(FileItem item:items){ // 2.5 如果FileItem不是文件类型 if (item.isFormField()) { } else { // 2.7 创建filemeta对象 temp = new FileMeta(); temp.setContent(item.getInputStream()); temp.setFileType(item.getContentType()); temp.setSize(item.getSize()/1024+ "Kb"); saveFile(temp,item,savePath); temp.setStatus("1"); String fileId=saveAttachment(temp); //保存到数据库 temp.setFileId(fileId); files.add(temp); } } } catch (FileUploadException e) { e.printStackTrace(); } } return files; } private void saveFile(FileMeta temp,FileItem item,String savePath){ String filename=item.getName(); //注意:不同的浏览器提交的文件名是不一样的,有些浏览器提交上来的文件名是带有路径的,如: c:\a\b\1.txt, //而有些只是单纯的文件名,如:1.txt filename = filename.substring(filename.lastIndexOf("\\")+1); temp.setName(filename); //得到上传文件的扩展名 // String fileExtName = filename.substring(filename.lastIndexOf(".")+1); //得到文件保存的名称 UUID String saveFilename=makeFileName(filename); temp.setFileName(saveFilename); //得到文件的保存目录 hash打散 String realSavePath = makePath(saveFilename, savePath); try { //获取item中的上传文件的输入流 InputStream in = item.getInputStream(); //创建一个文件输出流 FileOutputStream out = new FileOutputStream(realSavePath + "\\" + saveFilename); //创建一个缓冲区 byte buffer[] = new byte[1024]; //判断输入流中的数据是否已经读完的标识 int len = 0; //循环将输入流读入到缓冲区当中,(len=in.read(buffer))>0就表示in里面还有数据 while((len=in.read(buffer))>0){ //使用FileOutputStream输出流将缓冲区的数据写入到指定的目录(savePath + "\\" + filename)当中 out.write(buffer, 0, len); } //关闭输入流 关闭输出流 删除处理文件上传时生成的临时文件 in.close(); out.flush(); out.close(); item.delete(); } catch (Exception e) { temp.setError("上传失败,文件类型错误/超过上传大小限制"); e.printStackTrace(); } } private String saveAttachment(FileMeta temp){ Attachment attachment=new Attachment(); attachment.setAttachmentId(temp.getFileName()); attachment.setName(temp.getName()); attachment.setFileType(temp.getFileType()); attachment.setSize(temp.getSize()); String id=attachmentService.saveAttachment(attachment); return id; } /** * @Method: makeFileName * @Description: 生成上传文件的文件名,文件名以:uuid+"_"+文件的原始名称 */ private static String makeFileName(String filename) { // 2.jpg // 为防止文件覆盖的现象发生,要为上传文件产生一个唯一的文件名 return UUID.randomUUID().toString() + "_" + filename; } /** * 为防止一个目录下面出现太多文件,要使用hash算法打散存储 */ private static String makePath(String filename, String savePath) { // 得到文件名的hashCode的值,得到的就是filename这个字符串对象在内存中的地址 int hashcode = filename.hashCode(); int dir1 = hashcode & 0xf; // 0--15 int dir2 = (hashcode & 0xf0) >> 4; // 0-15 // 构造新的保存目录 String dir = savePath + "\\" + dir1 + "\\" + dir2; // upload\2\3 upload\3\5 // File既可以代表文件也可以代表目录 File file = new File(dir); // 如果目录不存在 if (!file.exists()) { // 创建目录 file.mkdirs(); } return dir; }FileMeta
@JsonIgnoreProperties({"content"}) public class FileMeta { private String fileName; private String fileType; private String size; private String thumbnailUrl; private String url; private String name; private String error; private String status; private InputStream content; private String fileId; public String getFileId() { return fileId; } public void setFileId(String fileId) { this.fileId = fileId; } public String getFileName() { return fileName; } public void setFileName(String fileName) { this.fileName = fileName; } public String getFileType() { return fileType; } public void setFileType(String fileType) { this.fileType = fileType; } public InputStream getContent() { return content; } public void setContent(InputStream content) { this.content = content; } public String getSize() { return size; } public void setSize(String size) { this.size = size; } public String getThumbnailUrl() { return thumbnailUrl; } public void setThumbnailUrl(String thumbnailUrl) { this.thumbnailUrl = thumbnailUrl; } public String getUrl() { return url; } public void setUrl(String url) { this.url = url; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getError() { return error; } public void setError(String error) { this.error = error; } public String getStatus() { return status; } public void setStatus(String status) { this.status = status; }
文件下载Servlet
package com.apusic.justice.wx.util; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.OutputStream; import java.net.URLEncoder; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; /** * 下载文件Servlet * * @author zhaoyang * */ public class DownLoadServlet extends HttpServlet { private static final long serialVersionUID = 1L; public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 得到要下载的文件名 request.setCharacterEncoding("UTF-8"); String fileName = request.getParameter("f"); // 23239283-92489-QQ图片.jpg // 上传的文件都是保存在/WEB-INF/upload目录下的子目录当中 String fileSaveRootPath = this.getServletContext().getRealPath("/WEB-INF/upload"); // 通过文件名找出文件的所在目录 String path = findFileSavePathByFileName(fileName, fileSaveRootPath); // 得到要下载的文件 File file = new File(path + "\\" + fileName); // 如果文件不存在 if (!file.exists()) { return; } // 处理文件名 String realname = fileName.substring(fileName.indexOf("_") + 1); // 设置响应头,控制浏览器下载该文件 response.setHeader("content-disposition", "attachment;filename=" + URLEncoder.encode(realname, "UTF-8")); // 读取要下载的文件,保存到文件输入流 FileInputStream in = new FileInputStream(path + "\\" + fileName); // 创建输出流 OutputStream out = response.getOutputStream(); // 创建缓冲区 byte buffer[] = new byte[1024]; int len = 0; // 循环将输入流中的内容读取到缓冲区当中 while ((len = in.read(buffer)) > 0) { // 输出缓冲区的内容到浏览器,实现文件下载 out.write(buffer, 0, len); } // 关闭文件输入流 in.close(); // 关闭输出流 out.close(); } /** * @Method: findFileSavePathByFileName * @Description: 通过文件名和存储上传文件根目录找出要下载的文件的所在路径 */ public String findFileSavePathByFileName(String filename, String saveRootPath) { int hashcode = filename.hashCode(); int dir1 = hashcode & 0xf; // 0--15 int dir2 = (hashcode & 0xf0) >> 4; // 0-15 String dir = saveRootPath + "\\" + dir1 + "\\" + dir2; // upload\2\3 upload\3\5 File file = new File(dir); if (!file.exists()) { // 创建目录 file.mkdirs(); } return dir; } public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } }
<servlet> <servlet-name>UploadServlet</servlet-name> <servlet-class>com.a_1.a_1.wx.util.UploadServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>UploadServlet</servlet-name> <url-pattern>/servlet/UploadServlet</url-pattern> </servlet-mapping> <servlet> <servlet-name>DownLoadServlet</servlet-name> <servlet-class>com.a_1.a_1.wx.util.DownLoadServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>DownLoadServlet</servlet-name> <url-pattern>/servlet/DownLoadServlet</url-pattern> </servlet-mapping>