一、认识FineUploader的功能:
进入示例网站:https://fineuploader.com/demos.html#basic-setup;
二、准备相关的js,css
Build Fine Uploader yourself from the GitHub repo
git clone https://github.com/FineUploader/fine-uploader.git
cd fine-uploader
npm install
make build
make build之后将生成一个“_build”目录,其中包含CSS、JS、图像和模板文件,以供所有可能构建的Fine Uploader。
上面是下载的fine-Uploader前台所需的代码。下面来看看后台代码:
三:服务器示例代码:
git clone https://github.com/FineUploader/server-examples/blob/master/java/UploadReceiver.java
我们所需要的java代码就在java那个目录下:
到现在,准备工作已经完成。
四、新建一个自己的web项目:
1、输入工程名,finish之后目录结构是这样的:
2、添加刚才在server-example中的java文件到src下:
然后这样直接会报错,应该在java文件中修改所在包名,并引入所有需要的jar包:
目前暂时引入了这些包。
3、接下来新建html了:
从刚才在git上下载的fine-Uploader文件夹中的client/html/templates中找到这次示例要用的html文件:gallery.html
(https://docs.fineuploader.com/features/styling.html 这里会涉及这几个文件的讲解)
<script type="text/template" id="qq-template"> <div class="qq-uploader-selector qq-uploader qq-gallery" qq-drop-area-text="Drop files here"> <div class="qq-total-progress-bar-container-selector qq-total-progress-bar-container"> <div role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100" class="qq-total-progress-bar-selector qq-progress-bar qq-total-progress-bar"></div> </div> <div class="qq-upload-drop-area-selector qq-upload-drop-area" qq-hide-dropzone> <span class="qq-upload-drop-area-text-selector"></span> </div> <div class="qq-upload-button-selector qq-upload-button"> <div>Upload a file</div> </div> <span class="qq-drop-processing-selector qq-drop-processing"> <span>Processing dropped files...</span> <span class="qq-drop-processing-spinner-selector qq-drop-processing-spinner"></span> </span> <ul class="qq-upload-list-selector qq-upload-list" role="region" aria-live="polite" aria-relevant="additions removals"> <li> <span role="status" class="qq-upload-status-text-selector qq-upload-status-text"></span> <div class="qq-progress-bar-container-selector qq-progress-bar-container"> <div role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100" class="qq-progress-bar-selector qq-progress-bar"></div> </div> <span class="qq-upload-spinner-selector qq-upload-spinner"></span> <div class="qq-thumbnail-wrapper"> <img class="qq-thumbnail-selector" qq-max-size="120" qq-server-scale> </div> <button type="button" class="qq-upload-cancel-selector qq-upload-cancel">X</button> <button type="button" class="qq-upload-retry-selector qq-upload-retry"> <span class="qq-btn qq-retry-icon" aria-label="Retry"></span> Retry </button> <div class="qq-file-info"> <div class="qq-file-name"> <span class="qq-upload-file-selector qq-upload-file"></span> <span class="qq-edit-filename-icon-selector qq-btn qq-edit-filename-icon" aria-label="Edit filename"></span> </div> <input class="qq-edit-filename-selector qq-edit-filename" tabindex="0" type="text"> <span class="qq-upload-size-selector qq-upload-size"></span> <button type="button" class="qq-btn qq-upload-delete-selector qq-upload-delete"> <span class="qq-btn qq-delete-icon" aria-label="Delete"></span> </button> <button type="button" class="qq-btn qq-upload-pause-selector qq-upload-pause"> <span class="qq-btn qq-pause-icon" aria-label="Pause"></span> </button> <button type="button" class="qq-btn qq-upload-continue-selector qq-upload-continue"> <span class="qq-btn qq-continue-icon" aria-label="Continue"></span> </button> </div> </li> </ul> <dialog class="qq-alert-dialog-selector"> <div class="qq-dialog-message-selector"></div> <div class="qq-dialog-buttons"> <button type="button" class="qq-cancel-button-selector">Close</button> </div> </dialog> <dialog class="qq-confirm-dialog-selector"> <div class="qq-dialog-message-selector"></div> <div class="qq-dialog-buttons"> <button type="button" class="qq-cancel-button-selector">No</button> <button type="button" class="qq-ok-button-selector">Yes</button> </div> </dialog> <dialog class="qq-prompt-dialog-selector"> <div class="qq-dialog-message-selector"></div> <input type="text"> <div class="qq-dialog-buttons"> <button type="button" class="qq-cancel-button-selector">Cancel</button> <button type="button" class="qq-ok-button-selector">Ok</button> </div> </dialog> </div> </script>
将上面的代码全部复制到新建的html文件:
然后在代码中引入fineUploader的核心js和css:
gallery.html必须使用fine-uploader-gallery.css文件,
以上是假如有图片上传的显示模板,那么得需要一个供图片拖动到的位置:(如示例网站):
此时此刻,我们需要对定义的该位置,“绑定”fine-Uploader组件:
<script type="text/javascript"> function createUploader() { uploader = new qq.FineUploader({ element: document.getElementById('fine-uploader-gallery'), autoUpload: true, //是否自动上传,true就会自动上传 multiple : true, //multiple选项:默认为true,用户可以一次选择多个文件并上传。 request: { //后台接受文件上传的URL路径。设置其中的endpoint属性。 endpoint: '/FineUploaderServerExample/servlet/UploadReceiver' }, /** session: { endpoint: '<%=request.getContextPath()%>/XXXX/XXXX', params: {'id': ${bulletin.id}} }, */ deleteFile: { //用于删除已上传的文件。可以删除的文件必须是在同一个页面中已经成功上传,或者由session选项初始化的文件列表。 enabled: true, endpoint: '/FineUploaderServerExample/servlet/UploadReceiver', //后台用于删除文件的URL地址。返回值和上传时一样必须含有“success”属性 method: 'POST',//必须设置为'post' forceConfirm: true, //是否出现删除确认的对话框,默认值为false confirmMessage: '确定要删除文件 {filename} 吗? 不可恢复!!', params: { foo: "bar" }, deletingFailedText: '删除失败!' }, text : { uploadButton : "<a href='#' class='add_files'><span class='icon-attachment'></span>浏览</a>" }, validation: { allowedExtensions: ['jpeg', 'jpg', 'gif', 'png'], itemLimit:12, sizeLimit: 2048 // 200 kB = 200 * 1024 bytes }, editFilename: { enabled: false }, onSubmit: function(id, fileName) { }, onUpload: function(id, fileName) { }, //loaded:表示已经上传到服务器端数据的大小[byte]. //total: 需要上传文件的大小. onProgress: function(id, fileName, loaded, total) { }, onComplete: function(id, fileName, responseJSON) { }, //id表示第几个开始上传的文件,Fine Uploder定义是默认从0开始计数. //fileName:上传文件的文件名. onCancel: function(id, fileName) { }, callbacks: { onAllComplete: function(successIDs, failIDs) { if(submitFile) submitdata(successIDs); } }, params:{ param1:"value1", param2:"value2" }, retry: { enableAuto: true }, debug:true }); } window.onload = createUploader; </script>
endpoint: '/FineUploaderServerExample/servlet/UploadReceiver',
endpoint这里:FineUploaderServerExample是工程名,/servlet/UploaderReceiver是访处理post请求的servlet的类。
至此,test.html就基本完成了。(然后加上web.xml)
4、修改刚才复制到项目中的server-example中的Java代码:
通过代码结构可以看出,UploadReceiver.java是直接处理上传和删除请求的类:
由于前台endpoint中请求的后台url都是post请求的,所以我们首先来看看doPost方法:
大致来看,第一步,根据请求的HttpRequest,临时存放文件的文件夹,上下文传入MultipartUploadParser这个类。
根据MultipartUploadParser类的构造方法,返回一个MultipartUploadParser实体。那我们看看在构造方法里是执行了什么操作:
public MultipartUploadParser(HttpServletRequest request, File repository, ServletContext context) throws Exception { //创建临时文件夹 if (!repository.exists() && !repository.mkdirs()) { throw new IOException("Unable to mkdirs to " + repository.getAbsolutePath()); } //servlet实现文件上传---核心API—DiskFileItemFactory fileItemsFactory = setupFileItemFactory(repository, context); //在setupFileItemFactory中指定临时文件目录,并给该目录设置了一个文件清理跟踪器 //临时文件在不再被使用的时候(如果相应的java.io.File是可回收的则更好)会自动被删除.(文件清理跟踪器) ServletFileUpload upload = new ServletFileUpload(fileItemsFactory); List<FileItem> formFileItems = upload.parseRequest(request); //将request进行解析并添加到FileItem中 parseFormFields(formFileItems); if (files.isEmpty()) { log.warn("No files were found when processing the requst. Debugging info follows."); writeDebugInfo(request); throw new FileUploadException("No files were found when processing the requst."); } else { if (log.isDebugEnabled()) { System.out.println("log.isDebugEnabled"); writeDebugInfo(request); } } }
获得一个 MultipartUploadParser之后,再据此获得一个RequestParser类的实例。
static RequestParser getInstance(HttpServletRequest request, MultipartUploadParser multipartUploadParser) throws Exception { RequestParser requestParser = new RequestParser(); if (multipartUploadParser == null) { if (request.getMethod().equals("POST") && request.getContentType() == null) { parseXdrPostParams(request, requestParser); } else { requestParser.filename = request.getParameter(FILENAME_PARAM); parseQueryStringParams(requestParser, request); } } else { requestParser.uploadItem = multipartUploadParser.getFirstFile(); requestParser.filename = multipartUploadParser.getFirstFile().getName(); //params could be in body or query string, depending on Fine Uploader request option properties parseRequestBodyParams(requestParser, multipartUploadParser); parseQueryStringParams(requestParser, request); } removeQqParams(requestParser.customParams); return requestParser; }
RequestParser的作用就是将前台传过来的文件信息获取,并填充自己的属性:
获得RequestParser之后,用writeFileForMultipartRequest方法写到指定位置:
private void writeFileForMultipartRequest(RequestParser requestParser) throws Exception { File dir = new File(UPLOAD_DIR, requestParser.getUuid());//在指定的文件夹下,创建一个子文件夹,名为对应的uuid dir.mkdirs(); System.out.println("writeFileForMultipartRequest === "); if (requestParser.getPartIndex() >= 0) { writeFile(requestParser.getUploadItem().getInputStream(), new File(dir, requestParser.getUuid() + "_" + String.format("%05d", requestParser.getPartIndex())), null); if (requestParser.getTotalParts() - 1 == requestParser.getPartIndex()) { File[] parts = getPartitionFiles(dir, requestParser.getUuid()); File outputFile = new File(dir, requestParser.getOriginalFilename());//timg.jpg for (File part : parts) { mergeFiles(outputFile, part); } assertCombinedFileIsVaid(requestParser.getTotalFileSize(), outputFile, requestParser.getUuid()); deletePartitionFiles(dir, requestParser.getUuid()); } } else { writeFile(requestParser.getUploadItem().getInputStream(), new File(dir, requestParser.getFilename()), null); } // option.add(++id, requestParser.getFilename(), requestParser.getUuid(), requestParser.getUuid().substring(5, 8)); }
//需要注意的是,这个文件夹如果没有指定绝对路径,那么遇到过的情况是:1、在桌面,2、在eclipse的安装目录。
我这写的是绝对路径。
写入的文件会这样存放:
每个文件的上层文件夹的名字就是其对应的uuid。
5、运行项目,添加缺少的文件,比如前台需要的gif图:
将报错的这几个文件都加入到css文件夹中。这样还可以完成删除操作。
这样就完成了fineuploader的例子使用。