• JAVA 前端大文件上传


    我们平时经常做的是上传文件,上传文件夹与上传文件类似,但也有一些不同之处,这次做了上传文件夹就记录下以备后用。

    首先我们需要了解的是上传文件三要素:

    1.表单提交方式:post (get方式提交有大小限制,post没有)

    2.表单的enctype属性:必须设置为multipart/form-data.

    3.表单必须有文件上传项:file,且文件项需要给定name值

    上传文件夹需要增加一个属性webkitdirectory,像这样:

    <input id="fileFolder" name="fileFolder" type="file"  webkitdirectory>

    不过webkitdirectory属性有个问题,只能支持高版本的chrome,不能支持低版本的IE,如ie6,ie7,ie8,不能做到全浏览器适配,运行环境比较单一。

    js中可以判断文件夹中文件数量及文件夹大小是否符合要求,不符合要求不能向后台提交:

    前台HTML模板

    this.GetHtmlFiles = function()

    {

         var acx = "";

         acx += '<div class="file-item" id="tmpFile" name="fileItem">

                    <div class="img-box"><img name="file" src="js/file.png"/></div>

                       <div class="area-l">

                           <div class="file-head">

                                <div name="fileName" class="name">HttpUploader程序开发.pdf</div>

                                <div name="percent" class="percent">(35%)</div>

                                <div name="fileSize" class="size" child="1">1000.23MB</div>

                        </div>

                           <div class="process-border"><div name="process" class="process"></div></div>

                           <div name="msg" class="msg top-space">15.3MB 20KB/S 10:02:00</div>

                       </div>

                       <div class="area-r">

                        <span class="btn-box" name="cancel" title="取消"><img name="stop" src="js/stop.png"/><div>取消</div></span>

                        <span class="btn-box hide" name="post" title="继续"><img name="post" src="js/post.png"/><div>继续</div></span>

                           <span class="btn-box hide" name="stop" title="停止"><img name="stop" src="js/stop.png"/><div>停止</div></span>

                           <span class="btn-box hide" name="del" title="删除"><img name="del" src="js/del.png"/><div>删除</div></span>

                       </div>';

         acx += '</div>';

         //文件夹模板

         acx += '<div class="file-item" name="folderItem">

                       <div class="img-box"><img name="folder" src="js/folder.png"/></div>

                       <div class="area-l">

                           <div class="file-head">

                                <div name="fileName" class="name">HttpUploader程序开发.pdf</div>

                                <div name="percent" class="percent">(35%)</div>

                                <div name="fileSize" class="size" child="1">1000.23MB</div>

                        </div>

                           <div class="process-border top-space"><div name="process" class="process"></div></div>

                           <div name="msg" class="msg top-space">15.3MB 20KB/S 10:02:00</div>

                       </div>

                       <div class="area-r">

                        <span class="btn-box" name="cancel" title="取消"><img name="stop" src="js/stop.png"/><div>取消</div></span>

                        <span class="btn-box hide" name="post" title="继续"><img name="post" src="js/post.png"/><div>继续</div></span>

                           <span class="btn-box hide" name="stop" title="停止"><img name="stop" src="js/stop.png"/><div>停止</div></span>

                           <span class="btn-box hide" name="del" title="删除"><img name="del" src="js/del.png"/><div>删除</div></span>

                       </div>';

         acx += '</div>';

         //上传列表

         acx += '<div class="files-panel" name="post_panel">

                       <div name="post_head" class="toolbar">

                           <span class="btn" name="btnAddFiles">选择多个文件</span>

                           <span class="btn" name="btnAddFolder">选择文件夹</span>

                           <span class="btn" name="btnPasteFile">粘贴文件和目录</span>

                           <span class="btn" name="btnSetup">安装控件</span>

                       </div>

                       <div class="content" name="post_content">

                           <div name="post_body" class="file-post-view"></div>

                       </div>

                       <div class="footer" name="post_footer">

                           <span class="btn-footer" name="btnClear">清除已完成文件</span>

                       </div>

                  </div>';

         return acx;

    };

    选择文件,选择文件夹,粘贴文件和文件夹的逻辑

    this.open_files = function (json)

    {

         for (var i = 0, l = json.files.length; i < l; ++i)

        {

             this.addFileLoc(json.files[i]);

         }

         setTimeout(function () { _this.PostFirst(); },500);

    };

    this.open_folders = function (json)

    {

        for (var i = 0, l = json.folders.length; i < l; ++i) {

            this.addFolderLoc(json.folders[i]);

        }

         setTimeout(function () { _this.PostFirst(); }, 500);

    };

    this.paste_files = function (json)

    {

         for (var i = 0, l = json.files.length; i < l; ++i)

         {

             this.addFileLoc(json.files[i]);

         }

    };

    后台在接收文件夹时不同之处在需要用MultipartHttpServletRequest

    boolean isMultipart = ServletFileUpload.isMultipartContent(request);

    FileItemFactory factory = new DiskFileItemFactory();  

    ServletFileUpload upload = new ServletFileUpload(factory);

    List files = null;

    try

    {

         files = upload.parseRequest(request);

    }

    catch (FileUploadException e)

    {// 解析文件数据错误 

        out.println("read file data error:" + e.toString());

        return;

      

    }

    FileItem rangeFile = null;

    // 得到所有上传的文件

    Iterator fileItr = files.iterator();

    // 循环处理所有文件

    while (fileItr.hasNext())

    {

         // 得到当前文件

         rangeFile = (FileItem) fileItr.next();

         if(StringUtils.equals( rangeFile.getFieldName(),"pathSvr"))

         {

             pathSvr = rangeFile.getString();

             pathSvr = PathTool.url_decode(pathSvr);

         }

    }

    server端的包和类

     

    文件块处页面,验证代码部分

    boolean verify = false;

    String msg = "";

    String md5Svr = "";

    long blockSizeSvr = rangeFile.getSize();

    if(!StringUtils.isBlank(blockMd5))

    {

         md5Svr = Md5Tool.fileToMD5(rangeFile.getInputStream());

    }

    verify = Integer.parseInt(blockSize) == blockSizeSvr;

    if(!verify)

    {

         msg = "block size error sizeSvr:" + blockSizeSvr + "sizeLoc:" + blockSize;

    }

    if(verify && !StringUtils.isBlank(blockMd5))

    {

         verify = md5Svr.equals(blockMd5);

         if(!verify) msg = "block md5 error";

    }

    if(verify)

    {

         //保存文件块数据

         FileBlockWriter res = new FileBlockWriter();

         //仅第一块创建

         if( Integer.parseInt(blockIndex)==1) res.CreateFile(pathSvr,Long.parseLong(lenLoc));

         res.write( Long.parseLong(blockOffset),pathSvr,rangeFile);

         up6_biz_event.file_post_block(id,Integer.parseInt(blockIndex));

        

         JSONObject o = new JSONObject();

         o.put("msg", "ok");

         o.put("md5", md5Svr); 

         o.put("offset", blockOffset);//基于文件的块偏移位置

         msg = o.toString();

    }

    rangeFile.delete();

    out.write(msg);

    生成文件名称的逻辑

    public String genFile(int uid, String md5,String nameLoc) throws IOException

    {

         SimpleDateFormat fmtDD = new SimpleDateFormat("dd");

         SimpleDateFormat fmtMM = new SimpleDateFormat("MM");

         SimpleDateFormat fmtYY = new SimpleDateFormat("yyyy");

        

         Date date = new Date();

         String strDD = fmtDD.format(date);

         String strMM = fmtMM.format(date);

         String strYY = fmtYY.format(date);

        

         String path = this.getRoot() + "/";

         path = path.concat(strYY);

         path = path.concat("/");

         path = path.concat(strMM);

         path = path.concat("/");

         path = path.concat(strDD);

         path = path.concat("/");

         path = path.concat(md5);

         path = path.concat(".");

         path = path.concat(PathTool.getExtention(nameLoc));

            

        

         File fl = new File(path);

        

         return fl.getCanonicalPath();//

    }

    以下是service层做的处理:

    整体模块划分如下:

     

    其中数据类实体逻辑处理如下

    public class FileInf {

         public FileInf(){}

         public String id="";

         public String pid="";

        public String pidRoot="";   

         /**  * 表示当前项是否是一个文件夹项。    */

         public boolean fdTask=false;        

         //   /// 是否是文件夹中的子文件  /// </summary>

         public boolean fdChild=false;

         /**  * 用户ID。与第三方系统整合使用。    */

         public int uid=0;

         /**  * 文件在本地电脑中的名称   */

         public String nameLoc="";

         /**  * 文件在服务器中的名称。   */

         public String nameSvr="";

         /**  * 文件在本地电脑中的完整路径。示例:D:SoftQQ2012.exe */

         public String pathLoc="";  

         /**  * 文件在服务器中的完整路径。示例:F:\ftp\uer\md5.exe     */

         public String pathSvr="";

         /**  * 文件在服务器中的相对路径。示例:/www/web/upload/md5.exe   */

         public String pathRel="";

         /**  * 文件MD5    */

         public String md5="";

         /**  * 数字化的文件长度。以字节为单位,示例:120125    */

         public long lenLoc=0;

         /**  * 格式化的文件尺寸。示例:10.03MB   */

         public String sizeLoc="";

         /**  * 文件续传位置。  */

         public long offset=0;

         /**  * 已上传大小。以字节为单位 */

         public long lenSvr=0;

         /**  * 已上传百分比。示例:10%  */

         public String perSvr="0%";

         public boolean complete=false;

         public Date PostedTime = new Date();

         public boolean deleted=false;

         /**  * 是否已经扫描完毕,提供给大型文件夹使用,大型文件夹上传完毕后开始扫描。  */

         public boolean scaned=false;

    }

    后台数据库中的逻辑基本上都用到了上面的实体类

    文件数据表操作类如下

    加载所有未完成的文件列表

    public String GetAllUnComplete(int f_uid)

    {

         StringBuilder sb = new StringBuilder();

         sb.append("select ");

         sb.append(" f_id");

         sb.append(",f_fdTask");    

         sb.append(",f_nameLoc");

         sb.append(",f_pathLoc");

         sb.append(",f_md5");

         sb.append(",f_lenLoc");

         sb.append(",f_sizeLoc");

         sb.append(",f_pos");

         sb.append(",f_lenSvr");

         sb.append(",f_perSvr");

         sb.append(",f_complete");

         sb.append(",f_pathSvr");//fix(2015-03-16):修复无法续传文件的问题。

         sb.append(" from up6_files ");//change(2015-03-18):联合查询文件夹数据

         sb.append(" where f_uid=? and f_deleted=0 and f_fdChild=0 and f_complete=0 and f_scan=0");//fix(2015-03-18):只加载未完成列表

         ArrayList<FileInf> files = new ArrayList<FileInf>();

         DbHelper db = new DbHelper();

         PreparedStatement cmd = db.GetCommand(sb.toString());

         try {

             cmd.setInt(1, f_uid);

             ResultSet r = db.ExecuteDataSet(cmd);

             while(r.next())

             {

                  FileInf f          = new FileInf();

                  f.uid              = f_uid;

                  f.id               = r.getString(1);

                  f.fdTask      = r.getBoolean(2);              

                  f.nameLoc          = r.getString(3);

                  f.pathLoc          = r.getString(4);

                  f.md5              = r.getString(5);

                  f.lenLoc      = r.getLong(6);

                  f.sizeLoc          = r.getString(7);

                  f.offset      = r.getLong(8);

                  f.lenSvr      = r.getLong(9);

                  f.perSvr      = r.getString(10);

                  f.complete         = r.getBoolean(11);

                  f.pathSvr     = r.getString(12);//fix(2015-03-19):修复无法续传文件的问题。

                  files.add(f);

                 

             }

             r.close();

             cmd.getConnection().close();

             cmd.close();

         } catch (SQLException e) {

             // TODO Auto-generated catch block

             e.printStackTrace();

         }

        

         if(files.size() < 1) return null;

        

         Gson g = new Gson();

        return g.toJson( files);//bug:arrFiles为空时,此行代码有异常

    }

    实现后的整体效果如下

     

    文件夹上传完后的效果

     

    服务器保存的文件夹数据,而且层级结构与本地客户端是一致的。这在OA系统中,或者网盘系统中使用时是非常有用的

     

    后端代码逻辑大部分是相同的,目前能够支持MySQL,Oracle,SQL。在使用前需要配置一下数据库,可以参考我写的这篇文章:http://blog.ncmem.com/wordpress/2019/08/12/java-http%E5%A4%A7%E6%96%87%E4%BB%B6%E6%96%AD%E7%82%B9%E7%BB%AD%E4%BC%A0%E4%B8%8A%E4%BC%A0/

    欢迎入群一起讨论“374992201”

  • 相关阅读:
    013.ES6 -对象字面量增强型写法
    012. ES6
    011. ES6 语法
    10. 9. Vue 计算属性的setter和getter 以及 计算属性的缓存讲解
    4. Spring MVC 数据响应方式
    3. SpringMVC 组件解析
    9. Vue 计算属性
    【洛谷 2984】给巧克力
    【洛谷 1821】捉迷藏 Hide and Seek
    【洛谷 1821】银牛派对Silver Cow Party
  • 原文地址:https://www.cnblogs.com/songsu/p/12626408.html
Copyright © 2020-2023  润新知