当前位置: 首页 > 后端技术 > Java

Java工程文件上传下载

时间:2023-04-02 00:19:35 Java

Java本地文件管理,主要是整理一下JavaRaddomAccessFile模块,话不多说,获取Java工程,运行。这是一个前后端分离的项目,前端比较简单,直接打开html文件。仓库地址:https://gitee.com/hicey/file-...提供:分段上传、断点续传、二次传输功能附加下载删除功能开发环境:JDK8、SpringBoot2.x、MySQL5.5、web-uploader第二次上传完成后,再次选择该文件即可启动即时传输功能。你可以在百度网盘等应用中看到类似的功能。基本的判断逻辑是记录文件的md5,通过文件md5判断文件是否已经上传。存在,如果存在,直接使用。代码逻辑有很多种。例如,1.使用redis或者mysql记录文件记录。根据唯一值md5,如果文件存在,则表示文件已经上传。直接添加一条文件记录,文件地址指向已有的文件地址。前端可以选择相应的库,如spark-md5.js,快速计算文件的md5。2、根据文件名和地址,找出磁盘中是否有相同的文件。如果有conf配置文件,需要一起判断。那么什么是md5?一个文件是否只对应一个唯一的md5?MD5即消息摘要算法5(Information-DigestAlgorithm5),用于保证信息传输的完整性和一致性。是计算机广泛使用的哈希算法之一(又译为摘要算法、散列算法),MD5一般在主流编程语言中都有实现。MD5算法具有以下特点:1、可压缩性:对于任意长度的数据,计算出的MD5值的长度是固定的,是一个32位的16进制字符串。2、计算方便:从原始数据中计算出MD5值很容易。3、抗修改:对原始数据的任何修改,即使只修改一个字节,也会导致MD5值相差很大。4.抗碰撞性强:知道原始数据及其MD5值,很难找到一个MD5值相同的数据(即伪造数据)。md5是一种常见的不可逆加密算法。它使用方便,计算速度快。用在很多场景,比如:给用户上传的文件命名,在数据库中保存用户密码,下载文件后检查文件是否正确等。图床前端和上传组件我不是很熟悉。可以选择的组件库有:webuploader.jsvue-simple-uploaderwebuploader链接地址:http://fex.baidu.com/webuploa...这里是webuploader.js,需要了解init函数和各种事件&回调。启动时,需要为后端恢复断点接口。其他的有默认值。同时fileUpload使用的是FormData,不需要字段校验,因为前后端库函数需要的字段是当前段,总段,md5名称等,这里有个疑问。前后端都使用库函数。字段怎么这么匹配?因为对前端不是很熟悉,所以从后端入手,可以考虑自定义类FormData,然后将字段匹配到后端库函数的UploadFileParam中。使用postman测试普通文件上传,即在body中选择form-data,选择file,然后上传文件,在java后台输入参数,得到MultipartFile接口,springframework封装。这里的实现类是StandardMultipartFile,它在org.springframework.web.multipart.support.StandardMultipartHttpServletRequest类下。普通文件上传比较简单,就是把http头中的Content-Disposition字段去掉,可以看成是后端继承InputStream,文件就是一个输入流。分片上传就是所谓的分片。前端可以分割文件。比如前端使用h5的Fileapi来读取文件并进行分割(啊,前端不熟悉,很多都是模糊的)。对于Java,后端处理是使用AddedRandomAccessFile来收集multipart上传的文件。上传test.mp4文件1.创建一个test.mp4.conf配置文件,RandomAccessFile,可读可写,用于存放所有chunk,当前chunk,当前chunk的flag。该标志已更新以指示它已上传。2.创建一个可读写的临时文件test.mp4.temp。每次追加到这个临时文件(追加分片文件),前端调用N次API上传,一点一点累积。当最后一个片段完成后,将文件重命名为test.mp4。3、“可选”前端调用add接口,表示要插入一条文件记录到mysql中。实际上,后端可以在最后一个片段完成后调用回调。最重要的是RandomAccessFile。与使用O_RDONLY的FileInputStream相比,FileOutputStream使用O_WRONLY|O_CREAT,RandomAccessFile在Java中提供了指定打开方式的能力。RandomAccessFile相当于FileInputStream和FileOutputStream的组合,可以读也可以写。而RandomAccessFile支持移动到文件的指定位置开始读写。导入java.io.File;导入java.io.IOException;导入java.io.RandomAccessFile;都是java.io包中的内容入口控制器代码@PostMapping(value="/breakpoint-upload",consumes="multipart/*",headers="content-type=multipart/form-data",produces="application/json;charset=UTF-8")publicRestResponsebreakpointResumeUpload(UploadFileParamparam,HttpServletRequestrequest){returnRestResponses.newResponseFromResult(fileService.breakpointResumeUpload(param,request));}consumes:指定提交的内容类型(Content-Type)用于处理请求,如application/json、text/html;produces:指定返回的内容类型,只有当request请求头中的(Accept)类型包含指定的类型时才返回;RandomAccessFile中比较重要的方法是setLength来设置文件长度。在这种情况下,它是设置conf的块。openjdk中用来记录所有片段的方法是:Java_java_io_RandomAccessFile_setLengthJNIEXPORTvoidJNICALLJava_java_io_RandomAccessFile_setLength(JNIEnv*env,jobjectthis,jlong??newLength){FDfd;jlong??cur;fd=getFD(环境,这个,raf_fd);if(fd==-1){JNU_ThrowIOException(env,"StreamClosed");返回;}如果((cur=IO_Lseek(fd,0L,SEEK_CUR))==-1)转到失败;如果(IO_SetLength(fd,newLength)==-1)转到失败;if(cur>newLength){if(IO_Lseek(fd,0L,SEEK_END)==-1)转到失败;}else{if(IO_Lseek(fd,cur,SEEK_SET)==-1)gotofail;}返回;fail:JNU_ThrowIOExceptionWithLastError(env,"setLengthfailed");}jinthandleSetLength(FDfd,jlong??length){intresult;RESTARTABLE(ftruncate64(fd,长度),结果);returnresult;}最后是操作系统层面的调用,调用ftruncate设置文件长度seek设置文件指针的偏移量,从文件开始算起,下一次读写操作发生的地方可以设置超出结尾的文件。设置文件末尾后的偏移量不会更改文件长度。只有当设置的偏移量超过文件末尾后,文件长度才会被写入改变。在openjdk中是seek0函数。Seek方法:在linux和unix操作系统中,就是??调用系统的lseek函数。如果lseek执行成功,会返回新的偏移量,所以打开文件的当前偏移量可以通过以下方法确定:write是openjdk中的writeBytes(b,off,len)。这三种写入方式和FileOutputStream是一样的,可以参考JDK源码阅读:FileOutputStream(下回。)inio表示有输入输出,Java运行在用户态,需要切换到内核态。这些都需要系统调用。有几种方法可以从用户模式切换到内核模式。系统调用这是用户态进程主动请求切换到内核态的一种方式。系统调用(SystemCall)是操作系统为运行在用户态的进程提供的与硬件设备(如CPU、磁盘、打印机等)进行交互的一组接口。当用户进程需要进行系统调用时,CPU通过软中断切换到内核态,开始执行内核系统调用函数。用户态进程通过系统调用请求使用操作系统提供的服务程序来完成工作。例如,print()实际上执行一个输出系统调用。异常当CPU正在执行一个运行在用户态的程序时,出现某种未知的异常,会触发当前运行的进程切换到处理该异常的内核相关程序,进而转入内核态。比如缺页异常。外围设备中断当外围设备完成了用户请求的操作后,就会向CPU发出相应的中断信号。此时CPU会暂停执行下一条要执行的指令,转而执行中断信号对应的处理程序。执行的指令是用户态的程序,所以从用户态到内核态的转换过程自然而然发生了。例如,当硬盘读写操作完成后,系统会切换到硬盘读写的中断处理程序来执行后续操作。如果恢复上传过程中断,则下次上传文件时,只会上传剩余的片段。设计逻辑大致是:1.判断conf文件是否存在。如果是,读取conf,确认当前chunk,返回前端。2、前端直接从当前chunk上传文件,继续。文件下载Stringfilename=(!EmptyUtils.basicIsEmpty(isSource)&&isSource)?fileDetails.getData().getFileName():fileDetails.getData().getFilePath();inputStream=fileService.getFileInputStream(id);response.setHeader("Content-Disposition","attachment;filename="+EncodingUtils.convertToFileName(request,filename));//获取输出流outputStream=response.getOutputStream();IOUtils.copy(inputStream,outputStream);文件预览(图片、PDF等)?controller@GetMapping(value="/view/{id}",produces=MediaType.IMAGE_PNG_VALUE)publicResponseEntityviewFilesImage(@PathVariableStringid)需要转换为byte[]publicstaticbyte[]inputStreamToByte(InputStreaminputStream)throwsIOException{ByteArrayOutputStreambyteArrayOutputStream=newByteArrayOutputStream();byte[]buff=newbyte[1024];内部rc=0;while((rc=inputStream.read(buff,0,1024))>0){byteArrayOutputStream.write(buff,0,钢筋混凝土);}返回byteArrayOutputStream.toByteArray();}总结Java的IO包内容很多,但大同小异。从JDK的角度看,是对操作系统文件的封装;从应用层Java的角度来看,就是处理输入输出,格式转换,因为场景比较多,所以分为很多类供开发者使用。其中RandomAccessFile适合上传大文件,还有其他常见的Files等等。