精华内容
下载资源
问答
  • WebUploader 文件上传 WebUploader是一个简单的以HTML5为主,FLASH为辅的现代文件上传组件。在现代的浏览器里面能充分发挥HTML5的优势,同时又不摒弃主流IE浏览器,延用原来的FLASH运行时,兼容IE6+,Andorid 4+,...
  • webuploader

    2018-01-16 10:39:52
    webuploader移动端文件上传功能,兼容ios、安卓,可以查看上传图片进度条
  • webUploader Demo源码下载

    热门讨论 2017-04-17 20:21:54
    web Uploader demo (有修改) 百度云及我遇到的问题汇总: https://blog.csdn.net/wx11408115/article/details/70215714
  • webuploader图片上传下载,PHP服务传输机制,Wampserver64部署后可直接运行。
  • 可以使用的webuploader上传实例,服务器返回值已可以打印出来
  • WebUploader例子

    2018-01-29 17:47:42
    官网给的文档太low了,这里我详细描述了webuploader的使用,供大家学习。
  • 最近遇见一个需要上传超大大文件的需求,调研了七牛和腾讯云的切片分段上传功能,因此在此整理前端大文件上传相关功能的实现。 在某些业务中,大文件上传是一个比较重要的交互场景,如上传入库比较大的Excel表格...

    最近遇见一个需要上传超大大文件的需求,调研了七牛和腾讯云的切片分段上传功能,因此在此整理前端大文件上传相关功能的实现。

    在某些业务中,大文件上传是一个比较重要的交互场景,如上传入库比较大的Excel表格数据、上传影音文件等。如果文件体积比较大,或者网络条件不好时,上传的时间会比较长(要传输更多的报文,丢包重传的概率也更大),用户不能刷新页面,只能耐心等待请求完成。

    下面从文件上传方式入手,整理大文件上传的思路,并给出了相关实例代码,由于PHP内置了比较方便的文件拆分和拼接方法,因此服务端代码使用PHP进行示例编写。

    本文相关示例代码位于github上,主要参考

    聊聊大文件上传

    大文件切割上传

    文件上传的几种方式

    首先我们来看看文件上传的几种方式。

    普通表单上传

    使用PHP来展示常规的表单上传是一个不错的选择。首先构建文件上传的表单,并指定表单的提交内容类型为enctype="multipart/form-data",表明表单需要上传二进制数据。

    然后编写index.php上传文件接收代码,使用move_uploaded_file方法即可(php大法好…)

    form表单上传大文件时,很容易遇见服务器超时的问题。通过xhr,前端也可以进行异步上传文件的操作,一般由两个思路。

    文件编码上传

    第一个思路是将文件进行编码,然后在服务端进行解码,之前写过一篇在前端实现图片压缩上传的博客,其主要实现原理就是将图片转换成base64进行传递

    varimgURL = URL.createObjectURL(file);

    ctx.drawImage(imgURL, 0, 0);

    // 获取图片的编码,然后将图片当做是一个很长的字符串进行传递

    vardata= canvas.toDataURL( "image/jpeg", 0.5);

    在服务端需要做的事情也比较简单,首先解码base64,然后保存图片即可

    $imgData = $_REQUEST[ 'imgData'];

    $base64 = explode( ',', $imgData)[ 1];

    $img = base64_decode($base64);

    $url = './test.jpg';

    if(file_put_contents($url, $img)) {

    exit(json_encode( array(

    url => $url

    )));

    }

    base64编码的缺点在于其体积比原图片更大(因为Base64将三个字节转化成四个字节,因此编码后的文本,会比原文本大出三分之一左右),对于体积很大的文件来说,上传和解析的时间会明显增加。

    更多关于base64的知识,可以参考Base64笔记。

    除了进行base64编码,还可以在前端直接读取文件内容后以二进制格式上传

    // 读取二进制文件

    functionreadBinary(text){

    vardata = newArrayBuffer(text.length);

    varui8a = newUint8Array(data, 0);

    for( vari = 0; i < text.length; i++){

    ui8a[i] = (text.charCodeAt(i) & 0xff);

    }

    console.log(ui8a)

    }

    varreader = newFileReader;

    reader. = function{

    readBinary( this.result) // 读取result或直接上传

    }

    // 把从input里读取的文件内容,放到fileReader的result字段里

    reader.readAsBinaryString(file);

    formData异步上传

    FormData对象主要用来组装一组用 发送请求的键/值对,可以更加灵活地发送Ajax请求。可以使用FormData来模拟表单提交。

    letfiles = e.target.files // 获取input的file对象

    letformData = newFormData;

    formData.append( 'file', file);

    axios.post(url, formData);

    服务端处理方式与直接form表单请求基本相同。

    iframe无刷新页面

    在低版本的浏览器(如IE)上,xhr是不支持直接上传formdata的,因此只能用form来上传文件,而form提交本身会进行页面跳转,这是因为form表单的target属性导致的,其取值有

    _self,默认值,在相同的窗口中打开响应页面

    _blank,在新窗口打开

    _parent,在父窗口打开

    _top,在最顶层的窗口打开

    framename,在指定名字的iframe中打开

    如果需要让用户体验异步上传文件的感觉,可以通过framename指定iframe来实现。把form的target属性设置为一个看不见的iframe,那么返回的数据就会被这个iframe接受,因此只有该iframe会被刷新,至于返回结果,也可以通过解析这个iframe内的文本来获取。

    functionupload{

    varnow = + newDate

    varid = 'frame'+ now

    $( "body").append( `<iframe style="display:none;" name="${id}" id="${id}" />`);

    var$form = $( "#myForm")

    $form.attr({

    "action": '/index.php',

    "method": "post",

    "enctype": "multipart/form-data",

    "encoding": "multipart/form-data",

    "target": id

    }).submit

    $( "#"+id).on( "load", function{

    varcontent = $( this).contents.find( "body").text

    try{

    vardata = JSON.parse(content)

    } catch(e){

    console.log(e)

    }

    })

    }

    大文件上传

    现在来看看在上面提到的几种上传方式中实现大文件上传会遇见的超时问题,

    表单上传和iframe无刷新页面上传,实际上都是通过form标签进行上传文件,这种方式将整个请求完全交给浏览器处理,当上传大文件时,可能会遇见请求超时的情形

    通过fromData,其实际也是在xhr中封装一组请求参数,用来模拟表单请求,无法避免大文件上传超时的问题

    编码上传,我们可以比较灵活地控制上传的内容

    大文件上传最主要的问题就在于:在同一个请求中,要上传大量的数据,导致整个过程会比较漫长,且失败后需要重头开始上传。试想,如果我们将这个请求拆分成多个请求,每个请求的时间就会缩短,且如果某个请求失败,只需要重新发送这一次请求即可,无需从头开始,这样是否可以解决大文件上传的问题呢?

    综合上面的问题,看来大文件上传需要实现下面几个需求

    支持拆分上传请求(即切片)

    支持断点续传

    支持显示上传进度和暂停上传

    接下来让我们依次实现这些功能,看起来最主要的功能应该就是切片了。

    文件切片

    参考: 大文件切割上传

    编码方式上传中,在前端我们只要先获取文件的二进制内容,然后对其内容进行拆分,最后将每个切片上传到服务端即可。

    在Java中,文件FIle对象是Blob对象的子类,Blob对象包含一个重要的方法slice,通过这个方法,我们就可以对二进制文件进行拆分。

    下面是一个拆分文件的示例,对于up6来说开发者不需要关心拆分的细节,由控件帮助实现,开发者只需要关心业务逻辑即可。

    控件上传的时候会为每一个文件块数据添加相关的信息,开发者在服务端接收到数据后可以自已进行处理。

    服务器接收到这些切片后,再将他们拼接起来就可以了,下面是PHP拼接切片的示例代码

    对于up6来说,开发人员不需要进行拼接,up6已经提供了示例代码,已经实现了这个逻辑。

    保证唯一性,控件会为每一个文件块添加信息,如块索引,块MD5,文件MD5

    断点续传

    up6自带续传功能,up6在服务端已经保存了文件的信息,在客户端也保存了文件的进度信息。在上传时控件会自动加载文件进度信息,开发者不需要关心这些细节。在文件块的处理逻辑中只需要根据文件块索引来识别即可。

    此时上传时刷新页面或者关闭浏览器,再次上传相同文件时,之前已经上传成功的切片就不会再重新上传了。

    服务端实现断点续传的逻辑基本相似,只要在getUploadSliceRecord内部调用服务端的查询接口获取已上传切片的记录即可,因此这里不再展开。

    此外断点续传还需要考虑切片过期的情况:如果调用了mkfile接口,则磁盘上的切片内容就可以清除掉了,如果客户端一直不调用mkfile的接口,放任这些切片一直保存在磁盘显然是不可靠的,一般情况下,切片上传都有一段时间的有效期,超过该有效期,就会被清除掉。基于上述原因,断点续传也必须同步切片过期的实现逻辑。

    续传效果

     

    上传进度和暂停

    通过xhr.upload中的progress方法可以实现监控每一个切片上传进度。

    上传暂停的实现也比较简单,通过xhr.abort可以取消当前未完成上传切片的上传,实现上传暂停的效果,恢复上传就跟断点续传类似,先获取已上传的切片列表,然后重新发送未上传的切片。

    由于篇幅关系,上传进度和暂停的功能这里就先不实现了。

    实现效果:

     

    小结

    目前社区已经存在一些成熟的大文件上传解决方案,如七牛SDK,腾讯云SDK等,也许并不需要我们手动去实现一个简陋的大文件上传库,但是了解其原理还是十分有必要的。

    本文首先整理了前端文件上传的几种方式,然后讨论了大文件上传的几种场景,以及大文件上传需要实现的几个功能

    通过Blob对象的slice方法将文件拆分成切片

    整理了服务端还原文件所需条件和参数,演示了PHP将切片还原成文件

    通过保存已上传切片的记录来实现断点续传

    还留下了一些问题,如:合并文件时避免内存溢出、切片失效策略、上传进度暂停等功能,并没有去深入或一一实现,继续学习吧

    后端代码逻辑大部分是相同的,目前能够支持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 

    展开全文
  • 支持文件和文件夹的批量下载,断点续传。刷新页面后继续传输。关闭浏览器后保留进度信息。 支持文件夹批量上传下载,服务器端保留文件夹层级结构,服务器端文件夹层级结构与本地相同。 支持大文件批量上传(20G)...

    一、 功能性需求与非功能性需求

    要求操作便利,一次选择多个文件和文件夹进行上传;
    支持PC端全平台操作系统,Windows,Linux,Mac

    支持文件和文件夹的批量下载,断点续传。刷新页面后继续传输。关闭浏览器后保留进度信息。

    支持文件夹批量上传下载,服务器端保留文件夹层级结构,服务器端文件夹层级结构与本地相同。

    支持大文件批量上传(20G)和下载,同时需要保证上传期间用户电脑不出现卡死等体验;
    支持文件夹上传,文件夹中的文件数量达到1万个以上,且包含层级结构。

    支持断点续传,关闭浏览器或刷新浏览器后仍然能够保留进度。

    支持文件夹结构管理,支持新建文件夹,支持文件夹目录导航

    交互友好,能够及时反馈上传的进度;

    服务端的安全性,不因上传文件功能导致JVM内存溢出影响其他功能使用;

    最大限度利用网络上行带宽,提高上传速度;


    二、 设计分析

    对于大文件的处理,无论是用户端还是服务端,如果一次性进行读取发送、接收都是不可取,很容易导致内存问题。所以对于大文件上传,采用切块分段上传

    从上传的效率来看,利用多线程并发上传能够达到最大效率。


    三、解决方案:

    文件上传页面的前端可以选择使用一些比较好用的上传组件,例如百度的开源组件WebUploader,泽优软件的up6,这些组件基本能满足文件上传的一些日常所需功能,如异步上传文件,文件夹,拖拽式上传,黏贴上传,上传进度监控,文件缩略图,甚至是大文件断点续传,大文件秒传。 

     

    在web项目中上传文件夹现在已经成为了一个主流的需求。在OA,或者企业ERP系统中都有类似的需求。上传文件夹并且保留层级结构能够对用户行成很好的引导,用户使用起来也更方便。能够提供更高级的应用支撑。

    文件夹数据表结构

    CREATE TABLE IF NOT EXISTS `up6_folders` (

      `f_id`               char(32) NOT NULL ,

      `f_nameLoc`               varchar(255) default '',

      `f_pid`                   char(32) default '',

      `f_uid`                   int(11) default '0',

      `f_lenLoc`           bigint(19) default '0',

      `f_sizeLoc`               varchar(50) default '0',

      `f_pathLoc`               varchar(255) default '',

      `f_pathSvr`               varchar(255) default '',

      `f_pathRel`               varchar(255) default '',

      `f_folders`               int(11) default '0',

      `f_fileCount`        int(11) default '0',

      `f_filesComplete`    int(11) default '0',

      `f_complete`              tinyint(1) default '0',

      `f_deleted`               tinyint(1) default '0',

      `f_time`                  timestamp NULL default CURRENT_TIMESTAMP on update CURRENT_TIMESTAMP,

      `f_pidRoot`               char(32) default '',

      PRIMARY KEY  (`f_id`)

    ) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ;

    文件数据表结构

    CREATE TABLE IF NOT EXISTS `up6_files` (

      `f_id`               char(32) NOT NULL,

      `f_pid`                   char(32) default '',        /*父级文件夹ID*/

      `f_pidRoot`               char(32) default '',        /*根级文件夹ID*/

      `f_fdTask`           tinyint(1) default '0',     /*是否是一条文件夹信息*/

      `f_fdChild`               tinyint(1) default '0',     /*是否是文件夹中的文件*/

      `f_uid`                   int(11) default '0',

      `f_nameLoc`               varchar(255) default '',    /*文件在本地的名称(原始文件名称)*/

      `f_nameSvr`               varchar(255) default '',    /*文件在服务器的名称*/

      `f_pathLoc`               varchar(512) default '',    /*文件在本地的路径*/

      `f_pathSvr`               varchar(512) default '',    /*文件在远程服务器中的位置*/

      `f_pathRel`               varchar(512) default '',

      `f_md5`                   varchar(40) default '',     /*文件MD5*/

      `f_lenLoc`           bigint(19) default '0',     /*文件大小*/

      `f_sizeLoc`               varchar(10) default '0',    /*文件大小(格式化的)*/

      `f_pos`                   bigint(19) default '0',     /*续传位置*/

      `f_lenSvr`           bigint(19) default '0',     /*已上传大小*/

      `f_perSvr`           varchar(7) default '0%',    /*已上传百分比*/

      `f_complete`              tinyint(1) default '0',     /*是否已上传完毕*/

      `f_time`                  timestamp NULL default CURRENT_TIMESTAMP on update CURRENT_TIMESTAMP,

      `f_deleted`               tinyint(1) default '0',

      `f_scan`                  tinyint(1) default '0',

      PRIMARY KEY  (`f_id`)

    ) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ;

    该项目核心就是文件分块上传。前后端要高度配合,需要双方约定好一些数据,才能完成大文件分块,我们在项目中要重点解决的以下问题。

    * 如何分片;

    * 如何合成一个文件;

    * 中断了从哪个分片开始。

    如何分,利用强大的js库,来减轻我们的工作,市场上已经能有关于大文件分块的轮子,虽然程序员的天性曾迫使我重新造轮子。但是因为时间的关系还有工作的关系,我只能罢休了。最后我选择了百度的WebUploader来实现前端所需。

    如何合,在合之前,我们还得先解决一个问题,我们如何区分分块所属那个文件的。刚开始的时候,我是采用了前端生成了唯一uuid来做文件的标志,在每个分片请求上带上。不过后来在做秒传的时候我放弃了,采用了Md5来维护分块和文件关系。

    在服务端合并文件,和记录分块的问题,在这方面其实行业已经给了很好的解决方案了。参考迅雷,你会发现,每次下载中的时候,都会有两个文件,一个文件主体,另外一个就是文件临时文件,临时文件存储着每个分块对应字节位的状态。

    这些都是需要前后端密切联系才能做好,前端需要根据固定大小对文件进行分片,并且请求中要带上分片序号和大小。前端发送请求顺利到达后台后,服务器只需要按照请求数据中给的分片序号和每片分块大小(分片大小是固定且一样的)算出开始位置,与读取到的文件片段数据,写入文件即可。

    为了便于开发,我 将服务端的业务逻辑进行了如下划分,分成初始化,块处理,文件上传完毕等。

    服务端的业务逻辑模块如下

     

    功能分析:

    文件夹生成模块

    文件夹上传完毕后由服务端进行扫描代码如下

    public class fd_scan

    {

        DbHelper db;

        Connection con;

        PreparedStatement cmd_add_f = null;

        PreparedStatement cmd_add_fd = null;

        public FileInf root = null;//根节点

       

        public fd_scan()

        {

            this.db = new DbHelper();

            this.con = this.db.GetCon();       

        }

       

        public void makeCmdF()

        {

            StringBuilder sb = new StringBuilder();

            sb.append("insert into up6_files (");

            sb.append(" f_id");//1

            sb.append(",f_pid");//2

            sb.append(",f_pidRoot");//3

            sb.append(",f_fdTask");//4

            sb.append(",f_fdChild");//5

            sb.append(",f_uid");//6

            sb.append(",f_nameLoc");//7

            sb.append(",f_nameSvr");//8

            sb.append(",f_pathLoc");//9

            sb.append(",f_pathSvr");//10

            sb.append(",f_pathRel");//11

            sb.append(",f_md5");//12

            sb.append(",f_lenLoc");//13

            sb.append(",f_sizeLoc");//14

            sb.append(",f_lenSvr");//15

            sb.append(",f_perSvr");//16

            sb.append(",f_complete");//17

           

            sb.append(") values(");

           

            sb.append(" ?");

            sb.append(",?");

            sb.append(",?");

            sb.append(",?");

            sb.append(",?");

            sb.append(",?");

            sb.append(",?");

            sb.append(",?");

            sb.append(",?");

            sb.append(",?");

            sb.append(",?");

            sb.append(",?");

            sb.append(",?");

            sb.append(",?");

            sb.append(",?");

            sb.append(",?");

            sb.append(",?");

            sb.append(")");

     

            try {

                this.cmd_add_f = this.con.prepareStatement(sb.toString());

                this.cmd_add_f.setString(1, "");//id

                this.cmd_add_f.setString(2, "");//pid

                this.cmd_add_f.setString(3, "");//pidRoot

                this.cmd_add_f.setBoolean(4, true);//fdTask

                this.cmd_add_f.setBoolean(5, false);//f_fdChild

                this.cmd_add_f.setInt(6, 0);//f_uid

                this.cmd_add_f.setString(7, "");//f_nameLoc

                this.cmd_add_f.setString(8, "");//f_nameSvr

                this.cmd_add_f.setString(9, "");//f_pathLoc

                this.cmd_add_f.setString(10, "");//f_pathSvr

                this.cmd_add_f.setString(11, "");//f_pathRel

                this.cmd_add_f.setString(12, "");//f_md5

                this.cmd_add_f.setLong(13, 0);//f_lenLoc

                this.cmd_add_f.setString(14, "");//f_sizeLoc

                this.cmd_add_f.setLong(15, 0);//f_lenSvr            

                this.cmd_add_f.setString(16, "");//f_perSvr

                this.cmd_add_f.setBoolean(17, true);//f_complete

            } catch (SQLException e) {

                // TODO Auto-generated catch block

                e.printStackTrace();

            }

        }

       

        public void makeCmdFD()

        {

            StringBuilder sb = new StringBuilder();

            sb.append("insert into up6_folders (");

            sb.append(" f_id");//1

            sb.append(",f_pid");//2

            sb.append(",f_pidRoot");//3

            sb.append(",f_nameLoc");//4

            sb.append(",f_uid");//5

            sb.append(",f_pathLoc");//6

            sb.append(",f_pathSvr");//7

            sb.append(",f_pathRel");//8

            sb.append(",f_complete");//9

            sb.append(") values(");//

            sb.append(" ?");

            sb.append(",?");

            sb.append(",?");

            sb.append(",?");

            sb.append(",?");

            sb.append(",?");

            sb.append(",?");

            sb.append(",?");

            sb.append(",?");

            sb.append(")");

     

            try {

                this.cmd_add_fd = this.con.prepareStatement(sb.toString());

                this.cmd_add_fd.setString(1, "");//id

                this.cmd_add_fd.setString(2, "");//pid

                this.cmd_add_fd.setString(3, "");//pidRoot

                this.cmd_add_fd.setString(4, "");//name

                this.cmd_add_fd.setInt(5, 0);//f_uid

                this.cmd_add_fd.setString(6, "");//pathLoc

                this.cmd_add_fd.setString(7, "");//pathSvr

                this.cmd_add_fd.setString(8, "");//pathRel

                this.cmd_add_fd.setBoolean(9, true);//complete

            } catch (SQLException e) {

                // TODO Auto-generated catch block

                e.printStackTrace();

            }

        }

       

        protected void GetAllFiles(FileInf inf,String root)

        {

            File dir = new File(inf.pathSvr);

            File [] allFile = dir.listFiles();

            for(int i = 0; i < allFile.length; i++)

            {

                if(allFile[i].isDirectory())

                {

                    FileInf fd = new FileInf();

                    String uuid = UUID.randomUUID().toString();

                    uuid = uuid.replace("-", "");

                    fd.id = uuid;

                    fd.pid = inf.id;

                    fd.pidRoot = this.root.id;

                    fd.nameSvr = allFile[i].getName();

                    fd.nameLoc = fd.nameSvr;

                    fd.pathSvr = allFile[i].getPath();

                    fd.pathSvr = fd.pathSvr.replace("\\", "/");

                    fd.pathRel = fd.pathSvr.substring(root.length() + 1);

                    fd.perSvr = "100%";

                    fd.complete = true;

                    this.save_folder(fd);

                   

                    this.GetAllFiles(fd, root);

                }

                else

                {

                    FileInf fl = new FileInf();

                    String uuid = UUID.randomUUID().toString();

                    uuid = uuid.replace("-", "");

                    fl.id = uuid;

                    fl.pid = inf.id;

                    fl.pidRoot = this.root.id;

                    fl.nameSvr = allFile[i].getName();

                    fl.nameLoc = fl.nameSvr;

                    fl.pathSvr = allFile[i].getPath();

                    fl.pathSvr = fl.pathSvr.replace("\\", "/");

                    fl.pathRel = fl.pathSvr.substring(root.length() + 1);

                    fl.lenSvr = allFile[i].length();

                    fl.lenLoc = fl.lenSvr;

                    fl.perSvr = "100%";

                    fl.complete = true;

                    this.save_file(fl);

                }

            }

        }

       

        protected void save_file(FileInf f)

        {      

            try {

                this.cmd_add_f.setString(1, f.id);//id

                this.cmd_add_f.setString(2, f.pid);//pid

                this.cmd_add_f.setString(3, f.pidRoot);//pidRoot

                this.cmd_add_f.setBoolean(4, f.fdTask);//fdTask

                this.cmd_add_f.setBoolean(5, true);//f_fdChild

                this.cmd_add_f.setInt(6, f.uid);//f_uid

                this.cmd_add_f.setString(7, f.nameLoc);//f_nameLoc

                this.cmd_add_f.setString(8, f.nameSvr);//f_nameSvr

                this.cmd_add_f.setString(9, f.pathLoc);//f_pathLoc

                this.cmd_add_f.setString(10, f.pathSvr);//f_pathSvr

                this.cmd_add_f.setString(11, f.pathRel);//f_pathRel

                this.cmd_add_f.setString(12, f.md5);//f_md5

                this.cmd_add_f.setLong(13, f.lenLoc);//f_lenLoc

                this.cmd_add_f.setString(14, f.sizeLoc);//f_sizeLoc

                this.cmd_add_f.setLong(15, f.lenSvr);//f_lenSvr        

                this.cmd_add_f.setString(16, f.perSvr);//f_perSvr

                this.cmd_add_f.setBoolean(17, f.complete);//f_complete

                this.cmd_add_f.executeUpdate();

            } catch (SQLException e) {

                // TODO Auto-generated catch block

                e.printStackTrace();

            }//

        }

       

        protected void save_folder(FileInf f)

        {

            try {

                this.cmd_add_fd.setString(1, f.id);//id

                this.cmd_add_fd.setString(2, f.pid);//pid

                this.cmd_add_fd.setString(3, f.pidRoot);//pidRoot

                this.cmd_add_fd.setString(4, f.nameSvr);//name

                this.cmd_add_fd.setInt(5, f.uid);//f_uid

                this.cmd_add_fd.setString(6, f.pathLoc);//pathLoc

                this.cmd_add_fd.setString(7, f.pathSvr);//pathSvr

                this.cmd_add_fd.setString(8, f.pathRel);//pathRel

                this.cmd_add_fd.setBoolean(9, f.complete);//complete

                this.cmd_add_fd.executeUpdate();

            } catch (SQLException e) {

                // TODO Auto-generated catch block

                e.printStackTrace();

            }

        }

       

        public void scan(FileInf inf, String root) throws IOException, SQLException

        {

            this.makeCmdF();

            this.makeCmdFD();

            this.GetAllFiles(inf, root);

            this.cmd_add_f.close();

            this.cmd_add_fd.close();

            this.con.close();

        }

    }

     

    分块上传,分块处理逻辑应该是最简单的逻辑了,up6已经将文件进行了分块,并且对每个分块数据进行了标识,这些标识包括文件块的索引,大小,偏移,文件MD5,文件块MD5(需要开启)等信息,服务端在接收这些信息后便可以非常方便的进行处理了。比如将块数据保存到分布式存储系统中

    分块上传可以说是我们整个项目的基础,像断点续传、暂停这些都是需要用到分块。

    分块这块相对来说比较简单。前端是采用了webuploader,分块等基础功能已经封装起来,使用方便。

    借助webUpload提供给我们的文件API,前端就显得异常简单。

    前台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;

    };

     

    分则必合。把大文件分片了,但是分片了就没有原本文件功能,所以我们要把分片合成为原本的文件。我们只需要把分片按原本位置写入到文件中去。因为前面原理那一部我们已经讲到了,我们知道分块大小和分块序号,我就可以知道该分块在文件中的起始位置。所以这里使用RandomAccessFile是明智的,RandomAccessFile能在文件里面前后移动。但是在andomAccessFile的绝大多数功能,已经被JDK1.4的NIO的“内存映射文件(memory-mapped files)”取代了。我在该项目中分别写了使用RandomAccessFile与MappedByteBuffer来合成文件。分别对应的方法是uploadFileRandomAccessFile和uploadFileByMappedByteBuffer。两个方法代码如下。

    秒传功能

    服务端逻辑

    秒传功能,相信大家都体现过了,网盘上传的时候,发现上传的文件秒传了。其实原理稍微有研究过的同学应该知道,其实就是检验文件MD5,记录下上传到系统的文件的MD5,在一个文件上传前先获取文件内容MD5值或者部分取值MD5,然后在匹配系统上的数据。

    Breakpoint-http实现秒传原理,客户端选择文件之后,点击上传的时候触发获取文件MD5值,获取MD5后调用系统一个接口(/index/checkFileMd5),查询该MD5是否已经存在(我在该项目中用redis来存储数据,用文件MD5值来作key,value是文件存储的地址。)接口返回检查状态,然后再进行下一步的操作。相信大家看代码就能明白了。

    嗯,前端的MD5取值也是用了webuploader自带的功能,这还是个不错的工具。

    控件计算完文件MD5后会触发md5_complete事件,并传值md5,开发者只需要处理这个事件即可,

     

    断点续传

    up6已经自动对断点续传进行了处理,不需要开发都再进行单独的处理。

    在f_post.jsp中接收这些参数,并进行处理,开发者只需要关注业务逻辑,不需要关注其它的方面。

     

    断点续传,就是在文件上传的过程中发生了中断,人为因素(暂停)或者不可抗力(断网或者网络差)导致了文件上传到一半失败了。然后在环境恢复的时候,重新上传该文件,而不至于是从新开始上传的。

    前面也已经讲过,断点续传的功能是基于分块上传来实现的,把一个大文件分成很多个小块,服务端能够把每个上传成功的分块都落地下来,客户端在上传文件开始时调用接口快速验证,条件选择跳过某个分块。

    实现原理,就是在每个文件上传前,就获取到文件MD5取值,在上传文件前调用接口(/index/checkFileMd5,没错也是秒传的检验接口)如果获取的文件状态是未完成,则返回所有的还没上传的分块的编号,然后前端进行条件筛算出哪些没上传的分块,然后进行上传。

    当接收到文件块后就可以直接写入到服务器的文件中

    这是文件夹上传完后的效果

    这是文件夹上传完后在服务端的存储结构

    参考文章: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”

    展开全文
  • webUploader上传demo

    万次阅读 多人点赞 2017-04-17 20:31:10
    (若工程中,是上传文件到阿里云... 最近,要做文件上传功能,在同事推荐下,选择了webUploader插件。WebUploader是由Baidu WebFE(FEX)团队开发的一个简单的以HTML5为主,FLASH为辅的现代文件上传组件,详情请看官...

    (若工程中,是上传文件到阿里云OSS中,可以去看这篇ossUploader
    最近,要做文件上传功能,在同事推荐下,选择了webUploader插件。WebUploader是由Baidu WebFE(FEX)团队开发的一个简单的以HTML5为主,FLASH为辅的现代文件上传组件,详情请看官网(直接百度webUploader,应该是第一个)。挺好用的,于是小结一下。

    1. 准备工作

    1.1 先搭建服务,选用wamp/xamp或其他服务
    1.2 下载webUploader	下载地址: https://github.com/fex-team/webuploader 
    

    2. 选用demo

    这里写图片描述
    点击选择图片(其他文件也可以)就可以上传啦!
    这里写图片描述

    遇到的几个问题

    1. 上传稍微大一点(10M+)的文件可能会有点问题,文件名被改,文件也会出错。文件被重命名成file_***

    2. 限制上传文件大小等配置

    3. 上传文件至服务器地址修改 (自定义)

    4. 在上传文件前事件

    4.1 上传前文件格式检测

    5. 根据自己需求要在上传前做验证

    6. 获取已添加的所有文件对象

    7.上传时,携带自定义参数

    8.上传大图片被压缩,而其他文件没有问题

    9. uploader.removeFile( file, true )无效, 执行getFiles()删除的文件还在file数组里

    10 . 上传时,动态改变上传服务器地址

    上传稍微大一点(10M+)的文件可能会有点问题,文件名被改,文件也会出错。文件被重命名成file_*,例如:file_58f4a2fc7dde0, 这问题也困扰我很久,在github webUploader的issue里,也发现有人有同样的问题,https://github.com/fex-team/webuploader/issues/263 但是我这里是没有解决。
    后来查找官方API才找到答案,原来是大文件没有切片导致啊!!!!
    这里写图片描述
    解决办法: 在upload.js 中,uploader实例化的时候设置切片chunked: true,
    这里写图片描述
    在这位置,可以更改一些配置,如看"点击上传图片"不顺眼,想改成“上传文件”,就在pick那改。
    还有的想把上传文件的大小限制改动下,也可以根据自己需求在这里的fileSizeLimit那里改。

    fileNumLimit {int} [可选] [默认值:undefined] 验证文件总数量, 超出则不允许加入队列。
    fileSizeLimit {int} [可选] [默认值:undefined] 验证文件总大小是否超出限制, 超出则不允许加入队列。
    fileSingleSizeLimit {int} [可选] [默认值:undefined] 验证单个文件大小是否超出限制, 超出则不允许加入队列。
    

    改成切片后,再上传文件,
    这里写图片描述
    成功! 完美。
    那这里又存在一个疑问,那文件上传的位置在哪里更改呢?
    这里写图片描述

    在server/fileupload.php 中改targetDir和targetDir 和targetDiruploadDir
    这里写图片描述
    这里写图片描述

    a. 在上传文件前事件

    //我自己的需求是,只能添加xml格式和zip格式文件 
    //webuploader.js
     input.on( 'change', function( e ) {  
                        var fn = arguments.callee,
                            clone;
                            //code                        
    
    
    /**下面检测文件格式的方法不推荐!不推荐!!! (webuploader实例化中有配置)
    	在file 对象中有个ext属性是文件格式的,可以用。
    */
    

    这里写图片描述

    上传前文件格式检测
    在webuploader实例化那里可以配置,详情看官网文档
    这里写图片描述
    我这里还有需要根据权限的不同去指定上传文件格式,我用个数组/字符串变量保存 (具体是字符串还是数组,请参看源码中怎么加,你就怎么加)
    这里写图片描述
    这里写图片描述

    实际上在这里加,只是在你点击添加文件时,那个弹框就给你过滤了其他文件,留下你指定的文件类型。
    如图。若用户吧把那个自定义文件下拉框换成全部文件,就可以添加其他文件了(刚刚自测了一把)。

    这里写图片描述

    显然,这与我们需求不符,uploader实例化配置accept只是个简单的过滤而已,
    所以还是得在文件添加到控件中,就自己检测。(个人觉得,就按我上面的那样,一旦用户添加进来了,就里面检测,添加不通过直接打回去)

    b. 根据自己需求要在上传前做验证

    //在662行左右,点击时做验证
          $upload.on('click', function() {
                if ( $(this).hasClass( 'disabled' ) ) {
                    return false;
                }
    

    这里写图片描述

    c. 获取已添加的所有文件对象

    //有时需要把所有文件做个判断啥的,就需要用到
    var fileItem = uploader.getFiles(); 
    //要文件添加进去了,才有用。在刚刚添加文件时,是无效的。
    //uploader是一个实例
    

    d. 上传时,携带自定义参数

    //见uploader实例化
    			uploader.options.formData.houseName = houseName;
                uploader.options.formData.houseType = houseType;
                uploader.options.formData.address = district; 
                //添加到实例化后就可以了,
    

    这里写图片描述
    这里写图片描述
    //地址瞎写的,
    e. 上传大图片被压缩,而其他文件没有问题
    之前自己检测过,上传大文件(非图片)到服务器上,与上传前文件对比大小,是没有问题的。 但是今天同事说,他传的图片(8M)被压缩(400kb左右)了,刚刚开始还不信,毕竟之前自己检测过,上传后的文件和原文件的大小一致。 后来查官方文档才知道真被压缩了。 文档是这么说的
    这里写图片描述
    解决办法: 在uploader实例化的时候,加个参数 compress: false;

    **9. uploader.removeFile( file, true )无效, 执行getFiles()删除的文件还在file数组里 **
    测试给我提个单,说删除有问题。
    额·····
    后来自己定位到,在点击删除时,执行到uploader.removeFile(file), 居然只是从视图中删除了? 没从file数组删除????
    什么鬼?
    这里写图片描述
    后来查官方API,说只要把removeFile()方法后面加个true就可以了。然并卵。。。。
    再去github issue查,这种问题还真有好几个,
    这里写图片描述
    不过都没有解决方法,最终找到个解决办法
    https://github.com/fex-team/webuploader/pull/2049/commits/b5461ee16deece92dbb34d2253f4c6ab3b215c1d
    但是写的我糊里糊涂,没整明白(也许是我理解能力不好),自己尝试几次,终于搞好了。所以就写下来,记录下。

    只要在webuploader.js 中加几行代码就好。

    1. 在webuploader.js 中搜到 function Queue() 方法
    2. 在里面添加 删除方法
    _delFile : function(file){
        for(var i = this._queue.length-1 ; i >= 0 ;i-- ){
          if(this._queue[i] == file){
             this._queue.splice(i,1);
             break;
          }
       }
    },
    
    1. 然后再在removeFile: function( file ) { 调用下就好了
      这里写图片描述
      最后附上一张gif,不骗你们啦~~~~
      在这里插入图片描述

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-doDypWhr-1569552244896)(https://img-blog.csdn.net/20170719164149880?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvd3gxMTQwODExNQ==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)]
    原有4个文件,删除一个,再删除一个,无压力啦~~~~~

    10 . 上传时,动态改变上传服务器地址

    我在一个实例化中,根据某种需求动态改变server地址。只要把uploader实例那个server配置改下就好了
    根据某个标记,然后动态改server地址(我在uploader实例化中,就把服务器地址给写好了,可以直接用。要是服务器地址比较多,就直接复制server地址吧。看个人)
    我是在点击上传那个事件中加的,

      $upload.on('click', function() { //700多行,搜下
    

    这里写图片描述
    demo下载地址(有删改,功能不变): 不贴了 (ps: 吐槽下csdn, 之前我自己上传这个demo,是0积分下载,貌似是上个月,它自己涨价到1积分,今天突然发现,nnd又涨价了,2积分。 奸商啊! )

    另附一个百度云下载地址:

    链接:http://pan.baidu.com/s/1boUSzhH 密码:z0l9

    另外,vue+ webuploader可以搞的。

    再次感谢百度团队贡献~~~

    展开全文
  • 1.在https://github.com/fex-team/webuploader 下载webuploader并解压, 解压后放到public里面。其中我把解压缩后的文件夹改名为webuploader,放到了public/static/文件夹下。 2. 将你放到public下的...

    https://github.com/fex-team/webuploader 下载webuploader并解压,
    解压后放到public里面。其中我把解压缩后的文件夹改名为webuploader,放到了public/static/文件夹下。

    将你放到public下的webuploader文件夹中的examples/imageupload/index.html复制到在对应需要文件上传功能的视图文件夹下面
    将其中的css和js文件路径进行替换,ThinkPHP5.0中 __STATIC__直接指向了项目下的public/static文件下
    下面这里的有点长,如果需要知道哪段是自己添加的 可搜索 ‘自己添加的’
    这几个字,有注释的

    <!DOCTYPE html>
    <html lang="zh-CN">
    <head>
        <meta charset="UTF-8">
        <title>WebUploader演示</title>
        <link rel="stylesheet" type="text/css" href="__STATIC__/webuploader/css/webuploader.css" />
        <link rel="stylesheet" type="text/css" href="__STATIC__/webuploader/examples/imageupload/style.css" />
    </head>
    <body>
    <!--这段form用于提交路径和其他表单信息 自己添加的-->
    <form action="{:url('gjourney/gjourney_add')}" method="post">
             <input type="hidden" id="image_url" name="image_url" />
             <input type="submit" value="提交">
        </form>
    <div id="wrapper">
        <div id="container">
            <!--头部,相册选择和格式选择-->
    
            <div id="uploader">
                <div class="queueList">
                    <div id="dndArea" class="placeholder">
                        <div id="filePicker"></div>
                        <p>或将照片拖到这里,单次最多可选300张</p>
                    </div>
                </div>
                <div class="statusBar" style="display:none;">
                    <div class="progress">
                        <span class="text">0%</span>
                        <span class="percentage"></span>
                    </div><div class="info"></div>
                    <div class="btns">
                        <div id="filePicker2"></div><div class="uploadBtn">开始上传</div>
                    </div>
                </div>
            </div>
        </div>
    </div>
    <script type="text/javascript" src="__STATIC__/webuploader/examples/imageupload/jquery.js"></script>
    <script type="text/javascript" src="__STATIC__/webuploader/dist/webuploader.js"></script>
    <script>//**其实这一段js就是upload.js粘过来的,改成自己的路径**
    
    	 var imgurl = new Array();//这个是自己添加的,用于把多张图片的路径放到这个jQuery数组中然后赋值到表单提交
    	 
    	 (function( $ ){
        // 当domReady的时候开始初始化
        $(function() {
            var $wrap = $('#uploader'),
    
                // 图片容器
                $queue = $( '<ul class="filelist"></ul>' )
                    .appendTo( $wrap.find( '.queueList' ) ),
    
                // 状态栏,包括进度和控制按钮
                $statusBar = $wrap.find( '.statusBar' ),
    
                // 文件总体选择信息。
                $info = $statusBar.find( '.info' ),
    
                // 上传按钮
                $upload = $wrap.find( '.uploadBtn' ),
    
                // 没选择文件之前的内容。
                $placeHolder = $wrap.find( '.placeholder' ),
    
                $progress = $statusBar.find( '.progress' ).hide(),
    
                // 添加的文件数量
                fileCount = 0,
    
                // 添加的文件总大小
                fileSize = 0,
    
                // 优化retina, 在retina下这个值是2
                ratio = window.devicePixelRatio || 1,
    
                // 缩略图大小
                thumbnailWidth = 110 * ratio,
                thumbnailHeight = 110 * ratio,
    
                // 可能有pedding, ready, uploading, confirm, done.
                state = 'pedding',
    
                // 所有文件的进度信息,key为file id
                percentages = {},
                // 判断浏览器是否支持图片的base64
                isSupportBase64 = ( function() {
                    var data = new Image();
                    var support = true;
                    data.onload = data.onerror = function() {
                        if( this.width != 1 || this.height != 1 ) {
                            support = false;
                        }
                    }
                    data.src = "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///ywAAAAAAQABAAACAUwAOw==";
                    return support;
                } )(),
    
                // 检测是否已经安装flash,检测flash的版本
                flashVersion = ( function() {
                    var version;
    
                    try {
                        version = navigator.plugins[ 'Shockwave Flash' ];
                        version = version.description;
                    } catch ( ex ) {
                        try {
                            version = new ActiveXObject('ShockwaveFlash.ShockwaveFlash')
                                    .GetVariable('$version');
                        } catch ( ex2 ) {
                            version = '0.0';
                        }
                    }
                    version = version.match( /\d+/g );
                    return parseFloat( version[ 0 ] + '.' + version[ 1 ], 10 );
                } )(),
    
                supportTransition = (function(){
                    var s = document.createElement('p').style,
                        r = 'transition' in s ||
                                'WebkitTransition' in s ||
                                'MozTransition' in s ||
                                'msTransition' in s ||
                                'OTransition' in s;
                    s = null;
                    return r;
                })(),
    
                // WebUploader实例
                uploader;
    
            if ( !WebUploader.Uploader.support('flash') && WebUploader.browser.ie ) {
    
                // flash 安装了但是版本过低。
                if (flashVersion) {
                    (function(container) {
                        window['expressinstallcallback'] = function( state ) {
                            switch(state) {
                                case 'Download.Cancelled':
                                    alert('您取消了更新!')
                                    break;
    
                                case 'Download.Failed':
                                    alert('安装失败')
                                    break;
    
                                default:
                                    alert('安装已成功,请刷新!');
                                    break;
                            }
                            delete window['expressinstallcallback'];
                        };
    
                        var swf = './expressInstall.swf';
                        // insert flash object
                        var html = '<object type="application/' +
                                'x-shockwave-flash" data="' +  swf + '" ';
    
                        if (WebUploader.browser.ie) {
                            html += 'classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" ';
                        }
    
                        html += 'width="100%" height="100%" style="outline:0">'  +
                            '<param name="movie" value="' + swf + '" />' +
                            '<param name="wmode" value="transparent" />' +
                            '<param name="allowscriptaccess" value="always" />' +
                        '</object>';
    
                        container.html(html);
    
                    })($wrap);
    
                // 压根就没有安转。
                } else {
                    $wrap.html('<a href="http://www.adobe.com/go/getflashplayer" target="_blank" border="0"><img alt="get flash player" src="http://www.adobe.com/macromedia/style_guide/images/160x41_Get_Flash_Player.jpg" /></a>');
                }
    
                return;
            } else if (!WebUploader.Uploader.support()) {
                alert( 'Web Uploader 不支持您的浏览器!');
                return;
            }
    
            // 实例化
            uploader = WebUploader.create({
                pick: {
                    id: '#filePicker',
                    label: '点击选择图片'
                },
                formData: {
                    uid: 123
                },
                dnd: '#dndArea',
                paste: '#uploader',
                swf: '__STATIC__/webuploader/dist/Uploader.swf',//改成自己的路径
                chunked: false,
                chunkSize: 512 * 1024,
                server: "{:url('gjourney/imageupload')}",//自己后台文件上传的路径
                // runtimeOrder: 'flash',
    
                // accept: {
                //     title: 'Images',
                //     extensions: 'gif,jpg,jpeg,bmp,png',
                //     mimeTypes: 'image/*'
                // },
    
                // 禁掉全局的拖拽功能。这样不会出现图片拖进页面的时候,把图片打开。
                disableGlobalDnd: true,
                fileNumLimit: 300,
                fileSizeLimit: 200 * 1024 * 1024,    // 200 M
                fileSingleSizeLimit: 50 * 1024 * 1024    // 50 M
            });
    
            // 拖拽时不接受 js, txt 文件。
            uploader.on( 'dndAccept', function( items ) {
                var denied = false,
                    len = items.length,
                    i = 0,
                    // 修改js类型
                    unAllowed = 'text/plain;application/javascript ';
    
                for ( ; i < len; i++ ) {
                    // 如果在列表里面
                    if ( ~unAllowed.indexOf( items[ i ].type ) ) {
                        denied = true;
                        break;
                    }
                }
    
                return !denied;
            });
    
            uploader.on('dialogOpen', function() {
                console.log('here');
            });
    
            // uploader.on('filesQueued', function() {
            //     uploader.sort(function( a, b ) {
            //         if ( a.name < b.name )
            //           return -1;
            //         if ( a.name > b.name )
            //           return 1;
            //         return 0;
            //     });
            // });
    
            // 添加“添加文件”的按钮,
            uploader.addButton({
                id: '#filePicker2',
                label: '继续添加'
            });
    
            uploader.on('ready', function() {
                window.uploader = uploader;
            });
    		
    		/**底下的uploadSuccess也是自己添加的,用来接收控制器返回的图片路径**/
    		uploader.on('uploadSuccess',function(file,response){
                
                imgurl.push(response);//控制器返回多张图片路径追加到刚开始声明的imgurl数组中
                $("#image_url").val(imgurl);//然后赋给hidden表单
           
               // alert(imgurl);
            });
    
            // 当有文件添加进来时执行,负责view的创建
            function addFile( file ) {
                var $li = $( '<li id="' + file.id + '">' +
                        '<p class="title">' + file.name + '</p>' +
                        '<p class="imgWrap"></p>'+
                        '<p class="progress"><span></span></p>' +
                        '</li>' ),
    
                    $btns = $('<div class="file-panel">' +
                        '<span class="cancel">删除</span>' +
                        '<span class="rotateRight">向右旋转</span>' +
                        '<span class="rotateLeft">向左旋转</span></div>').appendTo( $li ),
                    $prgress = $li.find('p.progress span'),
                    $wrap = $li.find( 'p.imgWrap' ),
                    $info = $('<p class="error"></p>'),
    
                    showError = function( code ) {
                        switch( code ) {
                            case 'exceed_size':
                                text = '文件大小超出';
                                break;
    
                            case 'interrupt':
                                text = '上传暂停';
                                break;
    
                            default:
                                text = '上传失败,请重试';
                                break;
                        }
    
                        $info.text( text ).appendTo( $li );
                    };
    
                if ( file.getStatus() === 'invalid' ) {
                    showError( file.statusText );
                } else {
                    // @todo lazyload
                    $wrap.text( '预览中' );
                    uploader.makeThumb( file, function( error, src ) {
                        var img;
    
                        if ( error ) {
                            $wrap.text( '不能预览' );
                            return;
                        }
    
                        if( isSupportBase64 ) {
                            img = $('<img src="'+src+'">');
                            $wrap.empty().append( img );
                        } else {
                            $.ajax('__STATIC__/webuploader/server/preview.php', {//这里的路径也改成自己自资源的路径
                                method: 'POST',
                                data: src,
                                dataType:'json'
                            }).done(function( response ) {
                                if (response.result) {
                                    img = $('<img src="'+response.result+'">');
                                    $wrap.empty().append( img );
                                } else {
                                    $wrap.text("预览出错");
                                }
                            });
                        }
                    }, thumbnailWidth, thumbnailHeight );
    
                    percentages[ file.id ] = [ file.size, 0 ];
                    file.rotation = 0;
                }
    
                file.on('statuschange', function( cur, prev ) {
                    if ( prev === 'progress' ) {
                        $prgress.hide().width(0);
                    } else if ( prev === 'queued' ) {
                        $li.off( 'mouseenter mouseleave' );
                        $btns.remove();
                    }
    
                    // 成功
                    if ( cur === 'error' || cur === 'invalid' ) {
                        console.log( file.statusText );
                        showError( file.statusText );
                        percentages[ file.id ][ 1 ] = 1;
                    } else if ( cur === 'interrupt' ) {
                        showError( 'interrupt' );
                    } else if ( cur === 'queued' ) {
                        $info.remove();
                        $prgress.css('display', 'block');
                        percentages[ file.id ][ 1 ] = 0;
                    } else if ( cur === 'progress' ) {
                        $info.remove();
                        $prgress.css('display', 'block');
                    } else if ( cur === 'complete' ) {
                        $prgress.hide().width(0);
                        $li.append( '<span class="success"></span>' );
                    }
    
                    $li.removeClass( 'state-' + prev ).addClass( 'state-' + cur );
                });
    
                $li.on( 'mouseenter', function() {
                    $btns.stop().animate({height: 30});
                });
    
                $li.on( 'mouseleave', function() {
                    $btns.stop().animate({height: 0});
                });
    
                $btns.on( 'click', 'span', function() {
                    var index = $(this).index(),
                        deg;
    
                    switch ( index ) {
                        case 0:
                            uploader.removeFile( file );
                            return;
    
                        case 1:
                            file.rotation += 90;
                            break;
    
                        case 2:
                            file.rotation -= 90;
                            break;
                    }
    
                    if ( supportTransition ) {
                        deg = 'rotate(' + file.rotation + 'deg)';
                        $wrap.css({
                            '-webkit-transform': deg,
                            '-mos-transform': deg,
                            '-o-transform': deg,
                            'transform': deg
                        });
                    } else {
                        $wrap.css( 'filter', 'progid:DXImageTransform.Microsoft.BasicImage(rotation='+ (~~((file.rotation/90)%4 + 4)%4) +')');
                        // use jquery animate to rotation
                        // $({
                        //     rotation: rotation
                        // }).animate({
                        //     rotation: file.rotation
                        // }, {
                        //     easing: 'linear',
                        //     step: function( now ) {
                        //         now = now * Math.PI / 180;
    
                        //         var cos = Math.cos( now ),
                        //             sin = Math.sin( now );
    
                        //         $wrap.css( 'filter', "progid:DXImageTransform.Microsoft.Matrix(M11=" + cos + ",M12=" + (-sin) + ",M21=" + sin + ",M22=" + cos + ",SizingMethod='auto expand')");
                        //     }
                        // });
                    }
    
    
                });
    
                $li.appendTo( $queue );
            }
    
            // 负责view的销毁
            function removeFile( file ) {
                var $li = $('#'+file.id);
    
                delete percentages[ file.id ];
                updateTotalProgress();
                $li.off().find('.file-panel').off().end().remove();
            }
    
            function updateTotalProgress() {
                var loaded = 0,
                    total = 0,
                    spans = $progress.children(),
                    percent;
    
                $.each( percentages, function( k, v ) {
                    total += v[ 0 ];
                    loaded += v[ 0 ] * v[ 1 ];
                } );
    
                percent = total ? loaded / total : 0;
    
    
                spans.eq( 0 ).text( Math.round( percent * 100 ) + '%' );
                spans.eq( 1 ).css( 'width', Math.round( percent * 100 ) + '%' );
                updateStatus();
            }
    
            function updateStatus() {
                var text = '', stats;
    
                if ( state === 'ready' ) {
                    text = '选中' + fileCount + '张图片,共' +
                            WebUploader.formatSize( fileSize ) + '。';
                } else if ( state === 'confirm' ) {
                    stats = uploader.getStats();
                    if ( stats.uploadFailNum ) {
                        text = '已成功上传' + stats.successNum+ '张照片至XX相册,'+
                            stats.uploadFailNum + '张照片上传失败,<a class="retry" href="#">重新上传</a>失败图片或<a class="ignore" href="#">忽略</a>'
                    }
    
                } else {
                    stats = uploader.getStats();
                    text = '共' + fileCount + '张(' +
                            WebUploader.formatSize( fileSize )  +
                            '),已上传' + stats.successNum + '张';
    
                    if ( stats.uploadFailNum ) {
                        text += ',失败' + stats.uploadFailNum + '张';
                    }
                }
    
                $info.html( text );
            }
    
            function setState( val ) {
                var file, stats;
    
                if ( val === state ) {
                    return;
                }
    
                $upload.removeClass( 'state-' + state );
                $upload.addClass( 'state-' + val );
                state = val;
    
                switch ( state ) {
                    case 'pedding':
                        $placeHolder.removeClass( 'element-invisible' );
                        $queue.hide();
                        $statusBar.addClass( 'element-invisible' );
                        uploader.refresh();
                        break;
    
                    case 'ready':
                        $placeHolder.addClass( 'element-invisible' );
                        $( '#filePicker2' ).removeClass( 'element-invisible');
                        $queue.show();
                        $statusBar.removeClass('element-invisible');
                        uploader.refresh();
                        break;
    
                    case 'uploading':
                        $( '#filePicker2' ).addClass( 'element-invisible' );
                        $progress.show();
                        $upload.text( '暂停上传' );
                        break;
    
                    case 'paused':
                        $progress.show();
                        $upload.text( '继续上传' );
                        break;
    
                    case 'confirm':
                        $progress.hide();
                        $( '#filePicker2' ).removeClass( 'element-invisible' );
                        $upload.text( '开始上传' );
    
                        stats = uploader.getStats();
                        if ( stats.successNum && !stats.uploadFailNum ) {
                            setState( 'finish' );
                            return;
                        }
                        break;
                    case 'finish':
                        stats = uploader.getStats();
                        if ( stats.successNum ) {
                            alert( '上传成功' );
                        } else {
                            // 没有成功的图片,重设
                            state = 'done';
                            location.reload();
                        }
                        break;
                }
    
                updateStatus();
            }
    
            uploader.onUploadProgress = function( file, percentage ) {
                var $li = $('#'+file.id),
                    $percent = $li.find('.progress span');
    
                $percent.css( 'width', percentage * 100 + '%' );
                percentages[ file.id ][ 1 ] = percentage;
                updateTotalProgress();
            };
    
            uploader.onFileQueued = function( file ) {
                fileCount++;
                fileSize += file.size;
    
                if ( fileCount === 1 ) {
                    $placeHolder.addClass( 'element-invisible' );
                    $statusBar.show();
                }
    
                addFile( file );
                setState( 'ready' );
                updateTotalProgress();
            };
    
            uploader.onFileDequeued = function( file ) {
                fileCount--;
                fileSize -= file.size;
    
                if ( !fileCount ) {
                    setState( 'pedding' );
                }
    
                removeFile( file );
                updateTotalProgress();
    
            };
    
            uploader.on( 'all', function( type ) {
                var stats;
                switch( type ) {
                    case 'uploadFinished':
                        setState( 'confirm' );
                        break;
    
                    case 'startUpload':
                        setState( 'uploading' );
                        break;
    
                    case 'stopUpload':
                        setState( 'paused' );
                        break;
    
                }
            });
    
            uploader.onError = function( code ) {
                alert( 'Eroor: ' + code );
            };
    
            $upload.on('click', function() {
                if ( $(this).hasClass( 'disabled' ) ) {
                    return false;
                }
    
                if ( state === 'ready' ) {
                    uploader.upload();
                } else if ( state === 'paused' ) {
                    uploader.upload();
                } else if ( state === 'uploading' ) {
                    uploader.stop();
                }
            });
    
            $info.on( 'click', '.retry', function() {
                uploader.retry();
            } );
    
            $info.on( 'click', '.ignore', function() {
                alert( 'todo' );
            } );
    
            $upload.addClass( 'state-' + state );
            updateTotalProgress();
        });
    
    })( jQuery );
    
    </script>
    </body>
    </html>
    

    如果直接引入的upload.js,而不是直接粘过来的话
    就需要找到webuploader/examples/imageupload/upload.js文件,在第154行,或者搜索server,将后台地址改为你想要的地址(其实就是指的文件上传的方法的地址),我的改成了’imgupload’ 注意经过我多次试验,’{:url(“”)}’助手函数不会被解析,这里不能使用。

    文件上传的后台方法
    将ThinkPHP5文档中关于图片上传的代码写进去,将请求的名称改成file。

    public function imgupload()
    	{
    		 $file = request()->file('file');
           // 移动到框架应用根目录/public/uploads/ 目录下
               $info = $file->move(ROOT_PATH . 'public' . DS . 'uploads');
              if($info){
                   // 成功上传后 获取上传信息
                   // 输出 jpg
                 
                    $data['path']=str_replace('\\',"/","/uploads/".$info->getSaveName()); 
                    
                    return json_encode($data['path']);//把路径ajax用json方式返回到视图中
                   
               }else{
                   // 上传失败获取错误信息
                   echo $file->getError();
               }
               
               
    	}
    

    具体上传到那个文件夹、上传大小类型后缀的验证、文件的命名规则等,根据自己需要进行书写。注意这里虽然是多图上传,但上传时候的循环已经被插件封装好了,不需要我们使用多图上传中的循环方式进行上传。

    上传界面如下
    这里写图片描述
    提交了,打印出来的路径
    这里写图片描述要改这个插件的大小和位置的话在style.css里的#wrapper里margin的属性值和witch的属性值,具体大小自己调。
    在这里插入图片描述

    展开全文
  • 在https://github.com/fex-team/webuploader 下载webuploader并解压, 解压后放到public里面。其中我把解压缩后的文件夹改名为webuploader,放到了public/文件夹下。 将你放到public下的webuploader文件夹中的...
  • 之前有使用过SWFUpload 做过上传图片功能,在本次实现过程中,有人推荐WebUploader 上传组件,因此采用web-Uploader 来实现文件上传功能。 WebUploader是由Baidu WebFE(FEX)团队开发的一个简单的以HTML5为主,FLASH...
  • webuploader.js大概4880行代码左右,在动态生成的input组件的下面(也可以直接搜索input),增加webkitdirectory属性。 function FileUploader(fileLoc, mgr) { var _this = this; this.id = fileLoc.id; this....
  • 1.下载webuploader 下载地址:官网http://fex.baidu.com/webuploader/ 2.引入webuploader 使用Web Uploader文件上传需要引入三种资源:JS, CSS, SWF。 方法1.直接引入文件 这里我们使用完全压缩版的webuploader.min...
  • webuploader断点续传

    2020-03-07 00:12:19
    webuploader断点续传,webuploader断点续传webuploader断点续传webuploader断点续传
  • webuploader.js大概4880行代码左右,在动态生成的input组件的下面(也可以直接搜索input),增加webkitdirectory属性。 function FileUploader(fileLoc, mgr) { var _this = this; this.id = fileLoc.id; this....
  • 因为webuploader是以ajax的形式上传文件,所以只会局部刷新,很好用哦~附上网址:http://fex.baidu.com/webuploader/doc/
  • webuploader.js大概4880行代码左右,在动态生成的input组件的下面(也可以直接搜索input),增加webkitdirectory属性。 function FileUploader(fileLoc, mgr) { var _this = this; this.id = fileLoc.id; this....
  • webuploader使用详解

    万次阅读 热门讨论 2017-10-10 16:52:27
    近日项目中用到了百度的图片上传插件。...官网下载地址:http://fex.baidu.com/webuploader/ 下载下来解压后你会看到如下文件 直接将这个文件夹丢在项目中public/admin/webuploader下 官网说了我
  • (一) asp.net WebUploader 上传

    万次阅读 2016-06-29 19:21:01
    系统环境:windows 7 ...webuploader 下载地址:http://fex.baidu.com/webuploader/download.html 网络环境:本机或局域网 测试结果:几十M 文件一般问题不大。1、web.config 代码: <configuratio
  • webuploader.zip

    2020-04-08 16:55:12
    webuploader 插件资源 包含了swf,css,js ,你们页可以去官网下载最新的:http://fex.baidu.com/webuploader/download.html 我这是权当存储了,哈哈哈
  • 前端webupload +后端 springboot 实现大文件的上传下载,可实现断点续传,秒传,亲测可用
  • webuploader.js大概4880行代码左右,在动态生成的input组件的下面(也可以直接搜索input),增加webkitdirectory属性。 function FileUploader(fileLoc, mgr) { var _this = this; this.id = fileLoc.id; this....
  • 需求:项目要支持大文件上传功能,经过讨论,初步将文件上传大小控制在500M内,因此自己需要在项目中进行文件上传部分的调整和配置,自己将大小都以501M来进行限制。 第一步: 前端修改 由于项目使用的是BJUI...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 2,434
精华内容 973
关键字:

webuploader下载