2020-03-27 11:08发布
首先我们来看看文件上传的几种方式。
普通表单上传
使用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>
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( ``);
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可以取消当前未完成上传切片的上传,实现上传暂停的效果,恢复上传就跟断点续传类似,先获取已上传的切片列表,然后重新发送未上传的切片。
由于篇幅关系,上传进度和暂停的功能这里就先不实现了。
实现效果:
上述的代码虽然可以成功将文件上传到服务器上面的指定目录当中,但是文件上传功能有许多需要注意的小细节问题,以下列出的几点需要特别注意的
1、为保证服务器安全,上传文件应该放在外界无法直接访问的目录下,比如放于WEB-INF目录下。
2、为防止文件覆盖的现象发生,要为上传文件产生一个唯一的文件名。
3、为防止一个目录下面出现太多文件,要使用hash算法打散存储。
4、要限制上传文件的最大值。
5、要限制上传文件的类型,在收到上传文件名时,判断后缀名是否合法。
针对上述提出的5点细节问题,我们来改进一下UploadHandleServlet,改进后的代码如下:
文件数据表结构
文件夹数据表结构
下载数据表结构
针对上述提出的5点小细节问题进行改进之后,我们的文件上传功能就算是做得比较完善了。
二、文件下载
加载文件列表,注意,这里只列出上传完的文件和文件夹,没有上传完的就不列出了
数据库的处理逻辑比较简单,注意一下SQL语句中的条件即可
我们要将Web应用系统中的文件资源提供给用户进行下载,首先我们要有一个页面列出上传文件目录下的所有文件,当用户点击文件下载超链接时就进行下载操作,编写一个ListFileServlet,用于列出Web应用系统中所有下载文件。
文件列表加载逻辑
这里简单说一下ListFileServlet中listfile方法,listfile方法是用来列出目录下的所有文件的,listfile方法内部用到了递归,在实际开发当中,我们肯定会在数据库创建一张表,里面会存储上传的文件名以及文件的具体存放目录,我们通过查询表就可以知道文件的具体存放目录,是不需要用到递归操作的,这个例子是因为没有使用数据库存储上传的文件名和文件的具体存放位置,而上传文件的存放位置又使用了散列算法打散存放,所以需要用到递归,在递归时,将获取到的文件名存放到从外面传递到listfile方法里面的Map集合当中,这样就可以保证所有的文件都存放在同一个Map集合当中。
编写一个用于处理文件下载的Servlet,DownLoadServlet的代码如下:
文件管理器主要逻辑如下:
文件管理器所有的逻辑都在PageFileMgr.java中实现。
另外,文件管理器有一些相关的配置在data目录下
系统配置数据如下
路径配置如下
文件管理器的脚本处理全部放在index.js中
现在有很多女生学习编程哦,尤其是前端,越来越多的女生学习,所以是合适的
html插入图片有两种方式:一种是通过标签插入的正常的图片,另一种是通过>< style>问题分析:1、首先你是通过第二种方式插入的是背景图片,直接用width和height只能控制div的宽度和高度。2、如果你插入的图片是通过标签的方式来插入的话,可以通过自身的属性...
html添加图片的方法如下:1、打开html软件。2、导入项目。3、找到要添加图片的位置。4、写【imgsrc=图片路径】即可。
1、默认状态下,背景会延伸到边框的区域下层2、可以通过 background-clip 属性来调整上述默认行为所带来的不便;这个属性的初始值是 border-box, 意味着背景会被元素的 border box(边框的外沿框) 裁切掉实现半透明边框代码如下:body { ...
html图片对齐主要用text-align属性来控制:text-align的值主要又分为:left左对齐,center居中对齐,right靠右对齐。
你没有设置好,具体解决方法如下。输入如下代码:width表示宽度height表示高度下图所示,我将图片的宽和高通通缩小,可以看到,图片的尺寸果然按照我的意愿进行了改变。
设置表格单元格的外边距为0 css中设置border-collapse: collapse; 或者在table标签中写cellspacing=0px
br是强制换行的,一般用在段落文字里边的换行。
1. 原生。直接告诉你,开发太慢,不能跨平台,先不要学。2. 使用WebView的hybrid app,Cordova/ionic 这些。就是用浏览器运行html,js,css仿app,我用了好几年,学习快,开发块,跨平台。问题是每个安卓版本/苹果版本/浏览器版本运行效果不一致,有些版本还运...
常用的移动APP开发框架:1、框架:PhoneGapPhoneGap是一个用基于HTML,CSS和JavaScript的,创建移动跨平台移动应用程序的快速开发平台。优点:可跨平台。phonegap框架帮我们解决了差异性,javascript与平台系统的连接由phonegap框架完成。 提供硬件访问控...
1. LungoJS 这个是被认为开发移动网站最好的HTML5开发框架,使用 HTML5/CSS3 和 JavaScript 技术。可帮助创建基于 iOS、 Android、 Blackberry 和 WebOS 平台的应用程序。该框架可利用当前移动设备的高级特性,可捕捉事件包括滑动、触屏、长按等。无需...
应该是样式不一样造成的
1、使用meta标签:viewportH5移动端页面自适应普遍使用的方法,理论上讲使用这个标签是可以适应所有尺寸的屏幕的,但是各设备对该标签的解释方式及支持程度不同造成了不能兼容所有浏览器或系统。viewport 是用户网页的可视区域。翻译为中文可以叫做视区。手机...
就是标签上的一个属性,比如:js获取的时候是:obj.getAttribute(data-type);做一些逻辑操作或者唯一标示等,很方便的。
src标签中放的是你的图片的路径,其中可以使用相对路径和绝对路径,相对路径就是你的图片在电脑中的存放位置,绝对路径就是指一些网站地址。alt标签中可以输入文本,当图片的路径错误时就会显示该文本内容。title标签中的值也是文本,当你鼠标悬停是所显示的...
最多设置5个标签!
首先我们来看看文件上传的几种方式。
普通表单上传
使用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>
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( ``);
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可以取消当前未完成上传切片的上传,实现上传暂停的效果,恢复上传就跟断点续传类似,先获取已上传的切片列表,然后重新发送未上传的切片。
由于篇幅关系,上传进度和暂停的功能这里就先不实现了。
实现效果:
上述的代码虽然可以成功将文件上传到服务器上面的指定目录当中,但是文件上传功能有许多需要注意的小细节问题,以下列出的几点需要特别注意的
1、为保证服务器安全,上传文件应该放在外界无法直接访问的目录下,比如放于WEB-INF目录下。
2、为防止文件覆盖的现象发生,要为上传文件产生一个唯一的文件名。
3、为防止一个目录下面出现太多文件,要使用hash算法打散存储。
4、要限制上传文件的最大值。
5、要限制上传文件的类型,在收到上传文件名时,判断后缀名是否合法。
针对上述提出的5点细节问题,我们来改进一下UploadHandleServlet,改进后的代码如下:
文件数据表结构
文件夹数据表结构
下载数据表结构
针对上述提出的5点小细节问题进行改进之后,我们的文件上传功能就算是做得比较完善了。
二、文件下载
加载文件列表,注意,这里只列出上传完的文件和文件夹,没有上传完的就不列出了
数据库的处理逻辑比较简单,注意一下SQL语句中的条件即可
我们要将Web应用系统中的文件资源提供给用户进行下载,首先我们要有一个页面列出上传文件目录下的所有文件,当用户点击文件下载超链接时就进行下载操作,编写一个ListFileServlet,用于列出Web应用系统中所有下载文件。
文件列表加载逻辑
这里简单说一下ListFileServlet中listfile方法,listfile方法是用来列出目录下的所有文件的,listfile方法内部用到了递归,在实际开发当中,我们肯定会在数据库创建一张表,里面会存储上传的文件名以及文件的具体存放目录,我们通过查询表就可以知道文件的具体存放目录,是不需要用到递归操作的,这个例子是因为没有使用数据库存储上传的文件名和文件的具体存放位置,而上传文件的存放位置又使用了散列算法打散存放,所以需要用到递归,在递归时,将获取到的文件名存放到从外面传递到listfile方法里面的Map集合当中,这样就可以保证所有的文件都存放在同一个Map集合当中。
编写一个用于处理文件下载的Servlet,DownLoadServlet的代码如下:
文件管理器主要逻辑如下:
文件管理器所有的逻辑都在PageFileMgr.java中实现。
另外,文件管理器有一些相关的配置在data目录下
系统配置数据如下
路径配置如下
文件管理器的脚本处理全部放在index.js中
相关问题推荐
现在有很多女生学习编程哦,尤其是前端,越来越多的女生学习,所以是合适的
html插入图片有两种方式:一种是通过标签插入的正常的图片,另一种是通过>< style>问题分析:1、首先你是通过第二种方式插入的是背景图片,直接用width和height只能控制div的宽度和高度。2、如果你插入的图片是通过标签的方式来插入的话,可以通过自身的属性...
html添加图片的方法如下:1、打开html软件。2、导入项目。3、找到要添加图片的位置。4、写【imgsrc=图片路径】即可。
1、默认状态下,背景会延伸到边框的区域下层2、可以通过 background-clip 属性来调整上述默认行为所带来的不便;这个属性的初始值是 border-box, 意味着背景会被元素的 border box(边框的外沿框) 裁切掉实现半透明边框代码如下:body { ...
html图片对齐主要用text-align属性来控制:text-align的值主要又分为:left左对齐,center居中对齐,right靠右对齐。
你没有设置好,具体解决方法如下。输入如下代码:width表示宽度height表示高度下图所示,我将图片的宽和高通通缩小,可以看到,图片的尺寸果然按照我的意愿进行了改变。
设置表格单元格的外边距为0 css中设置border-collapse: collapse; 或者在table标签中写cellspacing=0px
br是强制换行的,一般用在段落文字里边的换行。
1. 原生。直接告诉你,开发太慢,不能跨平台,先不要学。2. 使用WebView的hybrid app,Cordova/ionic 这些。就是用浏览器运行html,js,css仿app,我用了好几年,学习快,开发块,跨平台。问题是每个安卓版本/苹果版本/浏览器版本运行效果不一致,有些版本还运...
常用的移动APP开发框架:1、框架:PhoneGapPhoneGap是一个用基于HTML,CSS和JavaScript的,创建移动跨平台移动应用程序的快速开发平台。优点:可跨平台。phonegap框架帮我们解决了差异性,javascript与平台系统的连接由phonegap框架完成。 提供硬件访问控...
1. LungoJS 这个是被认为开发移动网站最好的HTML5开发框架,使用 HTML5/CSS3 和 JavaScript 技术。可帮助创建基于 iOS、 Android、 Blackberry 和 WebOS 平台的应用程序。该框架可利用当前移动设备的高级特性,可捕捉事件包括滑动、触屏、长按等。无需...
应该是样式不一样造成的
1、使用meta标签:viewportH5移动端页面自适应普遍使用的方法,理论上讲使用这个标签是可以适应所有尺寸的屏幕的,但是各设备对该标签的解释方式及支持程度不同造成了不能兼容所有浏览器或系统。viewport 是用户网页的可视区域。翻译为中文可以叫做视区。手机...
就是标签上的一个属性,比如:js获取的时候是:obj.getAttribute(data-type);做一些逻辑操作或者唯一标示等,很方便的。
src标签中放的是你的图片的路径,其中可以使用相对路径和绝对路径,相对路径就是你的图片在电脑中的存放位置,绝对路径就是指一些网站地址。alt标签中可以输入文本,当图片的路径错误时就会显示该文本内容。title标签中的值也是文本,当你鼠标悬停是所显示的...