Web文件操作:上传与下载

news2024/11/20 4:48:41

文件上传与下载

  • 文件上传
    • 文件上传的实现
      • 表结构设计
      • UploadFileServiceImpl的实现
      • 上传文件遇到的问题与解决
  • 文件下载

文件上传

文件上传的表单中,需要注意的三个地方:
1.表单的请求方式必须为post;
2.表单域必须有file类型;
3.表单的enctype属性值必须:multipart/form-data。

另外,下面简单介绍enctype属性以及其三个值:

  • enctype属性:规定form表单发送到服务器时的编码方式
  • application/x-www-form-urlencoded:默认编码方式
  • multipart/form-data:传输二进制类型的数据,如文件、图片等
  • text/plain:传输纯文体,不对特殊字符编码,空格转换为 “ + ”
<!-- 示例 -->
<form enctype="multipart/form-data" action="" method="post">
    请选择上传的文件:<input type="file" name=""><br>
    <input type="submit" value="上传">
</form>

如图:

在这里插入图片描述

文件上传的实现

文件上传的注意事项:

  • 为保证服务器安全,上传文件应该放在外界无法访问的目录下(WEB-INF目录下);
  • 限制上传文件的最大值(判断大小);
  • 限制上传文件的类型(判断验证);
  • 为防止文件覆盖现象,文件上传后产生唯一的文件名(UUID实现);
  • hash算法分散存储各个文件

如图所示:

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

了解到文件上传注意事项后,下面进行简单的实现。

表结构设计

uploadfiles表:记录上传文件的信息

id—记录id
filename—文件名
savename—文件保存的名字
desc—文件的描述
type—文件类型
size—文件大小
path—保存路径
time—保存时间

在这里插入图片描述

UploadFileServiceImpl的实现

首先,基于MVC思想构建主要结构;接着,创建类UploadFilePropertiesUtils来获取uploadfile.properties内容;最后,创建一个监听器,用来当服务器启动时执行contextInitialized的方法,遍历获取uploadfile.properties内容,并放入UploadFilePropertiesUtils类中的map里。

文件上传的基本思路:

package cn.edu.MVCcase.Model.service;

import cn.edu.MVCcase.JDBC.utils.UploadFilePropertiesUtils;
import cn.edu.MVCcase.Model.dao.FactoryDao;
import cn.edu.MVCcase.Model.dao.UploadFileDao;
import cn.edu.MVCcase.Model.model.UploadFile;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileUploadBase;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.util.Date;
import java.util.List;
import java.util.UUID;

public class UploadFileServiceImpl implements UploadFileService {

    UploadFileDao uploadFileDao = FactoryDao.getUploadFileDao();

    private String saveDirectory = UploadFilePropertiesUtils.getInstance().getProperties("savePath");
    private String tempDirectory = UploadFilePropertiesUtils.getInstance().getProperties("tempPath");
    private String sizeThreshold = UploadFilePropertiesUtils.getInstance().getProperties("sizeThreshold");
    private String fileSizeMax = UploadFilePropertiesUtils.getInstance().getProperties("fileSizeMax");
    private String sizeMax = UploadFilePropertiesUtils.getInstance().getProperties("sizeMax");
    private String fileExpection = UploadFilePropertiesUtils.getInstance().getProperties("fileExpection");

    @Override
    public void addUploadFile(UploadFile uploadFile) {
        //上传文件的信息保存到数据库前,先保存到指定的磁盘目录下
        uploadFileDao.addUploadFile(uploadFile);
    }

    @Override
    public List<UploadFile> getUploadFiles() {
       return uploadFileDao.getUploadFiles();
    }

    @Override
    public void deleteUploadFile(int id) {
        UploadFile uploadFile = uploadFileDao.get(id);
        //数据库的文件信息先删除
        uploadFileDao.deleteUploadFile(id);
        //再将磁盘上的文件删除
        deleteFile(uploadFile.getPath() + "\\" + uploadFile.getSavename());
    }

    @SuppressWarnings("unchecked")
    @Override
    public void saveUploadFile(HttpServletRequest req, HttpServletResponse resp) {
        //先将文件保存到指定的服务器目录中,再获取和创建保存文件的最终目录和临时目录
        String savePath = req.getSession().getServletContext().getRealPath(this.saveDirectory);
        String tempPath = req.getSession().getServletContext().getRealPath(this.tempDirectory);
        File tempFlie = new File(tempPath);
        if (!tempFlie.exists()) {
            tempFlie.mkdirs(); //若临时目录不存在,则创建一个
        }

        //创建DiskFileItemFactory工厂
        DiskFileItemFactory factory = new DiskFileItemFactory();
        factory.setSizeThreshold(Integer.parseInt(this.sizeThreshold)); //若小于100KB的上传文件存储在内存中;反之存储在tempPath
        factory.setRepository(tempFlie); //上传文件的临时目录

        //创建文件上传的解析器
        ServletFileUpload servletFileUpload = new ServletFileUpload(factory);
        servletFileUpload.setFileSizeMax(Integer.parseInt(this.fileSizeMax)); //限制单个文件上传的大小
        servletFileUpload.setHeaderEncoding("UTF-8"); //防止中文乱码
        servletFileUpload.setSizeMax(Integer.parseInt(this.sizeMax)); //限制多个文件同时上传的总和的大小

        //接着,解析结果返回一个List<FileItem>集合
        String filename = "";
        String desc = "";
        String type = "";
        long size = 0;
        String fileTypeException = "";
        String saveFilename = "";
        String realSavePath = "";
        OutputStream outputStream = null;
        InputStream inputStream = null;
        try {
            List<FileItem> fileItemList = servletFileUpload.parseRequest(req);
            if (fileItemList != null && fileItemList.size() > 0) {
                for (FileItem fileItem:fileItemList) {
                    if (fileItem.isFormField()) {
                        //判断该表单项是否是普通类型
                        desc = fileItem.getString("UTF-8");

                        //上传文件的信息写入到数据库uploadfiles表中
                        if (!"".equals(filename)) {
                            UploadFile uploadFile = new UploadFile();
                            uploadFile.setFilename(filename);
                            uploadFile.setSavename(saveFilename);
                            uploadFile.setDesc(desc);
                            uploadFile.setType(type);
                            uploadFile.setSize(size + "");
                            uploadFile.setPath(realSavePath);
                            uploadFile.setTime(new Date());
                            //插入
                            addUploadFile(uploadFile);
                        }
                    } else {
                        //否则该表单项是file类型
                        filename = fileItem.getName(); //获取文件名
                        type = fileItem.getContentType(); //获取文件类型
                        size = fileItem.getSize(); //获取文件大小

                        //处理绝对路径问题:浏览器不同可能会获取的filename带有绝对路径,截取最后的文件名
                        filename = filename.substring(filename.lastIndexOf("\\") + 1);
                        fileTypeException = filename.substring(filename.lastIndexOf(".") + 1); //获取文件后缀名

                        //验证后缀的合法性
                        if(this.fileExpection.indexOf(fileTypeException) == -1) {
                            throw new RuntimeException("不符合上传文件的类型,请上传后缀名为" + fileExpection + "的文件!!!");
                        }

                        //文件流写入保存的目录中
                        saveFilename = makeFileName(filename);
                        realSavePath = makePath(saveFilename,savePath);

                        //创建输出流
                        outputStream = new FileOutputStream(realSavePath + "\\" + saveFilename);
                        inputStream = fileItem.getInputStream();

                        //创建缓存区
                        byte[] buffer = new byte[1024];
                        int len = 0;
                        while ((len = inputStream.read(buffer)) > 0) {
                            outputStream.write(buffer, 0, len);
                        }
                        inputStream.close();
                        outputStream.close();
                    }
                }
            }

            //删除临时目录下的临时文件
            File tempfile = new File(tempPath);
            for (File file:tempfile.listFiles()) {
                file.delete();
            }
        } catch (FileUploadBase.FileSizeLimitExceededException e) {
            throw new RuntimeException("上传单个文件的大小超出了限制" + Integer.parseInt(this.fileSizeMax)/(1024*1024) + "MB!!!");
        } catch (FileUploadBase.SizeLimitExceededException e) {
            throw new RuntimeException("上传文件总大小超出了限制" + Integer.parseInt(this.sizeMax)/(1024*1024) + "MB!!!");
        } catch (Exception e) {
            throw new RuntimeException(e.getMessage());
        } finally {
            if(inputStream != null) {
                try {
                    inputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

            if(outputStream != null) {
                try {
                    outputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

        //判断提交的数据是否为上传表单的数据(enctype="multipart/form-data")
        if(!ServletFileUpload.isMultipartContent(req)) {
            throw new RuntimeException("上传表单的Form的编码方式不正确!!!");
        }
    }

    @Override
    public void deleteFile(String saveUploadFilePath) {
        //删除服务器上的上传文件
        File file = new File(saveUploadFilePath);
        if (file.isFile()) {
            file.delete();
        }
    }

    @Override
    public UploadFile getUploadFileById(int id) {
        return uploadFileDao.get(id);
    }

    //makeFileName、makePath对应的方法
    public String makeFileName(String filename) {
        //避免上传的文件同名问题
        return UUID.randomUUID().toString() + "_" + filename; //提供的一个自动生成主键 + "_" + filename
    }

    public String makePath(String saveFilename,String savePath) {
        //获得文件名的哈希值并转换为十六进制,确定在哈希表中的索引位置
        int hashcode = saveFilename.hashCode();
        int dir1 = hashcode & 0xf; //其值在0-15内
        int dir2 = (hashcode >> 4) & 0xf; //其值在0-15内

        //创建存储文件的新目录
        String dir = savePath + "\\" + dir1 + "\\" + dir2;
        File file = new File(dir);
        if (!file.exists()) {
            file.mkdirs();
        }
        return dir;
    }
}

注:上传文件的大小决定存储在最终保存目录还是临时目录下,而文件可以进行删除(若是临时文件即时删除;若不是,需要点击,数据库的文件信息与磁盘上的文件都会删除)。

上传文件遇到的问题与解决

jsp使用FileUpload上传文件设置setFileSizeMax或setSizeMax,若上传文件超过其设置的大小,浏览器链接会被重置。造成这个问题与Tomcat默认设置有关,需要在apache-tomcat-8.5.75\conf\server.xml下修改:

默认,如图:

在这里插入图片描述

修改后,如图:

connectionUploadTimeout=“36000000” //上传文件超时改为十个小时
disableUploadTimeout=“false” //关闭浏览器重置
maxSwallowSize=“-1” //吞吐量的最大值为无限大

在这里插入图片描述

文件下载

首先,获取文件的绝对路径与原来的文件名;接着,设置content-disposition响应头控制浏览器以下载的方式打开文件;最后下载到本地指定的目录下。

文件下载的基本思路:

private void downloadFile(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //获取文件的绝对路径
        int id = Integer.parseInt(req.getParameter("id"));
        UploadFile uploadFile = uploadFileService.getUploadFileById(id);
        String path = uploadFile.getPath() + "\\" + uploadFile.getSavename();
        //获取原来的文件名
        String filename = uploadFile.getFilename();
        //获取浏览器的类型
        String userAgent = req.getHeader("User-Agent");
        //不同浏览器的处理,解决中文乱码的问题
        if (userAgent.contains("MSIE") || userAgent.contains("Trident")) {
            filename = java.net.URLEncoder.encode(filename,"UTF-8"); //IE或以IE为内核的浏览器
        } else {
            filename = new String(filename.getBytes("UTF-8"),"ISO-8859-1"); //非IE或以IE为内核的浏览器
        }
        //设置content-disposition响应头控制浏览器以下载的方式打开文件
        resp.setHeader("content-disposition","attachment;filename=" + filename);
        //获取要下载的文件输入流
        InputStream inputStream = new FileInputStream(path);

        int len = 0;
        //创建缓冲区
        byte[] bytes = new byte[1024];
        //通过resp对象获取outputStream输出流对象
        OutputStream outputStream = resp.getOutputStream();
        //将FileInputStream流对象写入到bytes缓冲区
        while ((len=inputStream.read(bytes)) > 0) {
            outputStream.write(bytes,0,len);
        }
        //关闭
        inputStream.close();
        outputStream.close();
    }

结果如图:

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/147579.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

活动星投票奋斗青春,使命必达网络评选微信的投票方式线上免费投票

“奋斗青春&#xff0c;使命必达”网络评选投票_如何进行投票推广_参与投票活动_小程序的投票发展现在来说&#xff0c;公司、企业、学校更多的想借助短视频推广自己。通过微信投票小程序&#xff0c;网友们就可以通过手机拍视频上传视频参加活动&#xff0c;而短视频微信投票评…

英伟达528.02驱动发布支持4070 Ti!GFE新增9款游戏

自GTX 4070 Ti显卡发售后&#xff0c;英伟达便随即发布了支持该新显卡的Game Ready 528.02驱动&#xff0c;同时为《战意》和《达喀尔沙漠拉力赛》两款新游戏带来DLSS 3的支持&#xff0c;DLSS 3的队伍再度壮大&#xff01; 驱动人生现已支持英伟达Game Ready 528.02驱动&…

围绕http请求头中Referer展开的一些知识

1. 什么是referer&#xff1f; <点击以获取跳转信息 >跳转过去记得按一下f12点击网络请求详情&#xff0c;再刷新一下&#xff0c;就可以看见referer字段&#xff1a; 当我们尝试在浏览器内部直接输入这熟悉的网址时&#xff0c;此时刷新后则是这样一番景象&#xff1…

C++类和对象的基本概念

目录 1.c和c中struct的区别 2.类的封装 3.类的访问权限 1.c和c中struct的区别 c语言中结构体中不能存放函数,也就是数据(属性)和行为(方 法)是分离的 c中结构体中是可以存放函数的,也就是数据(属性)和行为 (方法)是封装在一起的 #define _CRT_SECURE_NO_WARNINGS #include …

基于Python tensorflow机器学习的人脸识别登陆系统源码、人脸注册系统源码

face_login 代码下载地址&#xff1a;基于Python tensorflow机器学习的人脸识别登陆系统源码、人脸注册系统源码 介绍 本项目基于tensorflow机器学习&#xff0c;实现web端人脸识别登陆&#xff0c;人脸注册。 提供手机端页面(face_login_app)和网页端页面(vue_element-adm…

JUC并发编程学习笔记(六)线程池及分支合并框架

10 ThreadPool 线程池&#xff08;重点&#xff09; 10.1 线程池简介 回顾以前的连接池概念 连接池是创建和管理一个连接的缓冲池的技术&#xff0c;这些连接准备好被任何需要它们的线程使用 线程池&#xff08;英语&#xff1a;thread pool&#xff09;&#xff1a;一种线程…

实时数仓,为什么不可代替?

什么是实时数据仓库&#xff1f;它有哪些不可替代之处&#xff1f; 大数据时代中&#xff0c;数据仓库解决了商业智能分析过程中的数据管理问题&#xff0c;但是存在烟囱式、冗余高的弊端 随着商业智能的兴起和数据时代的到来&#xff0c;越来越多的企业开始汇总、整合和分析自…

ArcGIS基础实验操作100例--实验62点、线、面状符号

本实验专栏参考自汤国安教授《地理信息系统基础实验操作100例》一书 实验平台&#xff1a;ArcGIS 10.6 实验数据&#xff1a;请访问实验1&#xff08;传送门&#xff09; 高级编辑篇--实验62 点、线、面状符号 目录 一、实验背景 二、实验数据 三、实验步骤 &#xff08;1&…

C/C++中二级指针传递参数【个人遇到内存值发生改变现象的记录及相关修正方法】

目录 0、前言 1、二级指针传参奇怪现象 2、分析 3、解决方法 0、前言 在c/c中&#xff0c;时常会使用到主调函数通过参数去获取被调函数中的数值情况。针对这种情况&#xff0c;我前面也写过C/C主调函数从被调函数中获取&#xff08;各种类型&#xff09;数据内容方式的梳理…

【ONE·R || 两次作业(一):R基础数据处理】

总言 两次作业汇报&#xff1a;其一。    文章目录总言1、作业一&#xff1a;1.1 、任务一&#xff1a;各项数据建立1.2 、任务二&#xff1a;去除缺失值1.3 、任务三&#xff1a;返回性别为女生&#xff0c;年龄<20的学生及成绩1.4、 任务四&#xff1a;统计性别为女生&a…

【Python百日进阶-数据分析】Day149 - plotly直方图:go.histogram()

文章目录4.2 利用 go.Histogram 的直方图4.2.1 基本直方图4.2.2 归一化直方图4.2.3 水平直方图4.2.4 叠加直方图4.2.5 堆叠直方图4.2.6 风格直方图4.2.7 直方图条形文本4.2.8 累积直方图4.2.9 指定聚合函数4.2.10 自定义分箱4.2.11 在直方图之间共享 bin4.2.12 按类别顺序排序直…

深度学习(一)-环境安装

前言&#xff1a; 最近电脑重装了下系统&#xff0c;然后所有环境啥的都得重新配置一遍&#xff0c;刚好趁着这个时间记录下整个环境的配置过程 注意&#xff1a;本文记录的仅为window系统的配置过程! 一、Anaconda安装及相关配置 Anaconda下载地址&#xff0c;根据需要选择需…

TypeScript 中 Class incorrectly implements interface 错误

当一个类在没有指定接口上定义的所有属性和方法的情况下实现接口时&#xff0c;会发生错误“Class incorrectly implements interface”。 要解决该错误&#xff0c;需要确保定义并键入接口的所有必需属性和方法。 下面是产生上述错误的示例代码 interface Employee {id: num…

Linux学习记录——유 gcc/g++基础知识

文章目录一、程序翻译二、gcc使用1、-o2、预处理-E3、编译-S4、汇编-c5、链接三、库四、库的部分实际操作五、Linux项目自动化构建工具 make/Makefile1、规则一、程序翻译 C语言中&#xff0c;写出代码后&#xff0c;编译器会经过四个阶段才会生成可执行文件。 预处理&#x…

计算数组中元素的加权平均值 numpy.average()

【小白从小学Python、C、Java】【计算机等级考试500强双证书】【Python-数据分析】计算数组中元素的加权平均值numpy.average()[太阳]选择题对于以下python代码最后输出的结果是?import numpy as npa np.array([1, 2, 3, 4])print("【显示】a")print(a)print("…

如何进行Java 单元测试

什么是单元测试 维基百科中是这样描述的&#xff1a;在计算机编程中&#xff0c;单元测试又称为模块测试&#xff0c;是针对程序模块来进行正确性检验的测试工作。程序单元是应用的最小可测试部件。在过程化编程中&#xff0c;一个单元就是单个程序、函数、过程等&#xff1b;…

架构师课程笔记day04——Nginx

大纲 1.从单体到集群过渡 2.Nginx 2.1什么是nginx 2.2常见服务器 2.3nginx在架构中所处位置 2.4使用率&#xff0c;性能&#xff0c;市场占有率等信息 2.5正反向代理啥意思 正向代理 反向代理 示例 2.6安装步骤 Nginx安装步骤 常用命令等 2.7请求链路 2.8进程模型 通用模型 …

JS面向对象基础(原型链、构造函数、new关键字、寄生组合继承、对象元编程)

这篇文章将简单介绍面向对象的基本概念&#xff0c;以及JS语言是如何支持面向对象这种编程范式的&#xff0c;最后还会讲解一些对象元编程的基础知识。通过阅读这篇文章&#xff0c;你可以了解JS中的原型链机制&#xff0c;new和构造函数的原理、寄生组合继承的实现以及对象元编…

李群李代数学习笔记

前言 因为论文学习的需要&#xff0c;入门了一下李群和李代数&#xff0c;觉得B站的这个视频讲得不错&#xff1a;视频地址为机器人学——李群、李代数快速入门&#xff0c;这里记录一下。 前言引入&#xff1a;一些常见的例子S1S^1S1&#xff1a;单位复数SO(2)SO(2)SO(2)&…

ArcGIS基础实验操作100例--实验64创建统计图符号

本实验专栏参考自汤国安教授《地理信息系统基础实验操作100例》一书 实验平台&#xff1a;ArcGIS 10.6 实验数据&#xff1a;请访问实验1&#xff08;传送门&#xff09; 高级编辑篇--实验64 创建统计图符号 目录 一、实验背景 二、实验数据 三、实验步骤 &#xff08;1&am…