当前位置: 首页 > news >正文

JavaEE简单示例——文件的上传和下载

文件的上传和下载的实现原理的简单介绍

表单的构成

首先,我们先来介绍我们的需要用到的表单,在这个表单中,首先值得我们注意的就是,在type为file的input标签中.这个控件是我们主要用来选择上传的文件的, 除此之外,我们要想实现文件的上传,还需要将method的属性的值设置为post,只有当请求方式为post的时候才能携带数据.以及enctype属性的值设置为multipart/form-data, 表示告诉浏览器以流的方式来处理我们的数据,这样在后台才能进行解析. 简单来说,在页面中,要想实现文件的上传需要进行必要的三个设置:

  • 将input标签的type属性设置为file
  • 将form标签的method的属性的值设置为post
  • 将form标签的enctype属性的值设置为multipart/form-data

还有一点就是在H5之后,出现了一个新的属性叫做multiple,这个属性用在文件上传框中,可以一次上传多个文件.

然后,就是用来展示已经上传的文件的列表 列表的实现非常的简答,我们只只需要在页面加载的时候向后台发送一个请求,向负责文件读取的控制器请求一下文件列表,然后将获取的文件列表追加到表格中, 这个文件的编写比较重要的就是from属性的配置,以及在追加文件列表的时候注意标签和变量的拼接

页面代码:

<%--Created by IntelliJ IDEA.User: 33680Date: 2023/3/29Time: 8:25To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" pageEncoding="UTF-8" %>
<html>
<head><title>文件的上传和下载</title><script src="${pageContext.request.contextPath}/js/jquery-3.6.4.min.js"type="text/javascript"></script><style>table{border: 2px solid black;}td{border: 1px solid black;}</style>
</head>
<body>
<form action="${pageContext.request.contextPath}/fileUplode" method="post" enctype="multipart/form-data"><table><tr><td width="200" align="center">文件上传${msg}</td><td width="200" align="center">下载列表</td></tr><tr><td><input type="file" name="files" multiple="multiple"><input type="reset" value="清空"><input type="submit" value="提交"></td><td id="files"></td></tr></table>
</form>
<script>$(document).ready(function (){var url = "${pageContext.request.contextPath}/getFileName";$.get(url,function (files){var files = eval('('+files+')');for(var i = 0;i<files.length;i++){$("#files").append("<li><a href=${pageContext.request.contextPath}"+"\/"+"download?filename="+files[i].name+">"+files[i].name+"</a></li>")}})})
</script>
</body>
</html>

文件上传

在文件的上传这部分,首先我们需要获取文件的列表,所以我们需要使用SpringMVC的MultipartFile类来封装,要使用这个类,我们首先需要在配置文件中配置多部件解析器

多部件解析器

多部件解析器就是一个JavaBean,所以我们需要在spring-mvc.xml文件中进行配置,需要注意的是,这个JavaBean的id的值是固定的,所以我们在配置的时候需要注意 然后我们就可以配置其他属性了,我们使用依赖注入的方式向里面注入配置参数:

  • defaultEncoding:这个属性是配置请求的编码格式,一般为UTF-8
  • maxUploadSize:这个属性是限制上上传的文件的大小,单位是字节

在配置完成多部件解析器之后,我们就可以顺利的使用SpringMVC的MultipartFile类来处理上传的文件了. 对于这个类的使用,我们使用最频繁的方法主要有两个:

  • isEmpty():这个方法用来判断上传的文件是否为空
  • transferTo(File file):这个方法的功能是保存文件

文件上传的基本流程是:首先,我们从页面中上传文件到后台,然后后台使用MultipartFile类来封装文件,如果文件不为空,则将其使用transferTo()方法保存到我们设置的路径中.

在上传文件时对重名文件的处理

在我们上传文件的时候,可能会遇到重名的文件,对于重名的文件,我们要做一些设置改变他原有的名字.首先,我们要创建一个文件叫做files.json文件这个文件的作用是存放我们的文件名字. 在我们保存文件之前,我们要先读取这个文件,对于这个文件的读写操作,我们都封装进了一个工具类中,可以方便我们对文件信息类的操作, 之后,判断读取到的文件内容,如果内容为空,则表示是第一个文件,直接保存.如果文件中有内容,我们就将数据信息文件中的内容读取出来,并且封装进一个类中,更加方便我们在Java中操作这个数据信息. 然后我们会判断文件名是否重名,如果重名了,则将原来的文件名以"."进行分割,然后在中间拼接上一个"(1)",变成一个新的文件名进行保存.

文件下载

对于文件的下载就比较简单了,基本的格式都是固定的

  1. 首先我们设置被下载的文件所在的文件夹的路径
  2. 一般,我们要下载文件的时候会携带一个下载文件的请求,我们就可以使用文件夹路径和请求中携带的文件名参数拼接成唯一的文件路径,然后通过文件路径创建文件对象
  3. 创建响应头对象,然后设置响应头,告诉浏览器我要对文件进行下载,并且以流的形式处理文件
  4. 最后,我们使用Spring的ResponseEntity类对下载的文件进行处理操作,直接返回ResponseEntity类的对象,流处理的文件,响应头对象和响应状态码

文件名的的编码转换

我们接收到的文件的编码可能会导致出错,所以我们可能需要对文件名进行转码,转码的方式也很简单:

  • 首先我们要判断发送请求的浏览器是否为火狐,如果是火狐我们需要按照火狐的方式修改编码
  • 然后就是判断如果不是火狐,则直接使用普通的方式修改文件编码即可

文件列表的展示

在之前我们上传文件的时候,我们会将上传的文件的文件名写入到一个json文件中,一个是用来判断文件是否重名,另一个作用是我们可以读取里面的信息, 传递到页面中形成一个列表,让我们的对文件有一个更直观的了解. 对于列表的展示,我们只需要获取json文件的路径,然后使用我们之前创建的JSON文件读取写入工具类,将我们的JSON文件的内容读取到页面中,然后在页面中对读取的数据进行处理之后展示出来即可

JSON文件操作工具类:

package SpringMVC.Upload_and_download.utils;
//这个包是在之前我们学过的方便我们对文件的读写操作的类
import org.apache.commons.io.IOUtils;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;//这个类的作用是方便我们对JSON文件的读取操作
public class JSONFileUtils {
//    读取文件中的内容public static String readFile(String filepath) throws IOException {
//        根据传递进来的参数,创建文件输出流,将文件中的内容输出获取出来FileInputStream fis = new FileInputStream(filepath);return IOUtils.toString(fis);}
//    向文件中写入内容public static void writeFile(String data , String filepath)throws Exception{
//        根据传递进来的参数,创建文件输出流,将内容写入到文件FileOutputStream fos = new FileOutputStream(filepath);IOUtils.write(data,fos);}
}

JSON文件映射类:

package SpringMVC.Upload_and_download;
//这个类的作用是为了方便Json内容的读取,我们创建与Json内容对应的实体类方便我们在Java代码中的操作
public class Resource {private String name;@Overridepublic String toString() {return "Resource{" +"name='" + name + '\'' +'}';}public String getName() {return name;}public void setName(String name) {this.name = name;}public Resource(String name) {this.name = name;}public Resource() {}
}

 文件上传下载控制器:

package SpringMVC.Upload_and_download.utils;import SpringMVC.Upload_and_download.Resource;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.commons.io.FileUtils;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;
import com.fasterxml.jackson.core.type.TypeReference;
import sun.misc.BASE64Encoder;
import javax.servlet.http.HttpServletRequest;
import java.io.File;
import java.io.IOException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;//这个类的作用是实现文件的上传和下载
@Controller
public class FileController {/**** @param files 保存上传的多个文件列表* @param request 当前请求的信息* @return 返回的是响应的页面,以请求转发的方式* @throws Exception 抛出多个异常,直接抛出最大的异常*/
//    文件的上传@RequestMapping("/fileUplode")public String fileUplode(MultipartFile[] files , HttpServletRequest request) throws Exception {
//存放文件存放的路径String path = request.getServletContext().getRealPath("/") + "files/";
//      创建JSON转换类的对象ObjectMapper om = new ObjectMapper();
//        判断上传的文件是否为空if(files!=null && files.length>0){
//            如果文件列表不为空,并且长度大于零,则表示有文件上传,正常进行处理for(MultipartFile file : files){//首先获取上传文件的文件名,创建一个列表存放文件信息
//                getOriginalFilename()方法是用来获取上传时的文件名String filename = file.getOriginalFilename();List<Resource> list = new ArrayList<>();
//                读取文件files.json文件中的数据,判断上传的文件名是否重名String json = JSONFileUtils.readFile(path+"/files.json");
//                先判断JSON文件中是否有数据,如果没有数据就表示这是第一个上传的文件,肯定不会重名System.out.println(json.length());if(json.length()!=0){
//                    将JSON文件中的数据转换成集合,方便我们的判断操作list = om.readValue(json, new TypeReference<List<Resource>>() {});for(Resource resource : list){
//                        进行判断if (filename != null && filename.equals(resource.getName())) {String []strings = filename.split("\\.");filename = strings[0]+"(1)."+strings[1];System.out.println(filename);}}}
//                文件保存的完整路径String Filepath = path+filename;
//                开始保存文件file.transferTo(new File(Filepath));list.add(new Resource(filename));
//                将集合中的数据转换成jsonjson = om.writeValueAsString(list);
//                将上传的文件名称保存在file.json中JSONFileUtils.writeFile(json,path+"/files.json");System.out.println(json);System.out.println(list.toString());}
//            将文件的上传状态传递到页面中request.setAttribute("msg","上传成功");return "forward:/SpringMVC/pages/FIle.jsp";}
//        当我们没有进入第一个if时,则表示所有的状态都是失败状态,所以我们此处返回失败的状态信息request.setAttribute("msg","上传失败");return "forward:/SpringMVC/pages/FIle.jsp";}//    实现文件列表的功能@ResponseBody@RequestMapping(value = "/getFileName" , produces = "text/html;charset=utf-8")public String getFileName(HttpServletRequest request) throws IOException {
//        获取项目的路径String path = request.getServletContext().getRealPath("/")+"files/files.json";return JSONFileUtils.readFile(path);}//    在实现文件下载之前,我们需要先去实现一个解决文件乱码的问题public String getFileName(HttpServletRequest request,String filename)throws Exception{
//        由于各浏览器支持的编码格式转换的方式不一样,主要体现在火狐浏览器的配置不一样,所以这里需要获取请求头中携带的浏览器型号是不是火狐BASE64Encoder base64Encoder = new BASE64Encoder();String agent = request.getHeader("User-Agent");if(agent.equals("Firefox")){
//            如果是火狐,则需要使用火狐规则的浏览器转码格式filename = "=?UTF-8?B?"+ base64Encoder.encode(filename.getBytes(StandardCharsets.UTF_8)) +"?=";}else {
//            其他的就使用统一的转码方式filename = URLEncoder.encode(filename,"UTF-8");}
//        最后将转码之后的文件名返回即可return filename;}//    实现文件的下载@RequestMapping("/download")public ResponseEntity<byte[]> fileDownload(HttpServletRequest request, String filename)throws Exception{
//        需要下载的文件的根路径String path = request.getServletContext().getRealPath("/files/");
//        filename = new String(filename.getBytes(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8);
//        根据传递进来的数据不同下载不同的文件File file = new File(path+File.separator+filename);
//        创建设置响应头的对象HttpHeaders headers = new HttpHeaders();
//        将文件名进行转码处理filename = this.getFileName(request,filename);
//        告诉浏览器我要对这个文件进行下载处理headers.setContentDispositionFormData("attachment",filename);
//        以流的形式处理文件headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
//        最后返回读取到的文件,在浏览器中就可以显示为下载
//        使用SpringMVC的ResponseEntity处理封装的数据return new ResponseEntity<>(FileUtils.readFileToByteArray(file), headers, HttpStatus.OK);}
}

文件目录结构:

 

注意点

对于文件的上传和下载,我们主要知道大概的流程,和我们需要做的事情即可,可以看到大部分的流程都是固定的,并且大部分都是工具类的使用,所以我们如果实在记不住具体的操作可以随时来复制就可以.

相关文章:

JavaEE简单示例——文件的上传和下载

文件的上传和下载的实现原理的简单介绍 表单的构成 首先,我们先来介绍我们的需要用到的表单,在这个表单中,首先值得我们注意的就是,在type为file的input标签中.这个控件是我们主要用来选择上传的文件的, 除此之外,我们要想实现文件的上传,还需要将method的属性的值设置为post…...

【C语言督学训练营 第五天】数组字符串相关知识

文章目录前言一、数组的定义1.一维数组①.如何定义②.声明规则③.内存分布④.初始化方法2.二维数组3.高维数组二、访问数组元素相关问题1.访问越界2.数组的传递三、Scanf与字符数组1.字符数组初始化2.scanf读取字符四、字符数组相关函数前言 今天的C语言训练营没有安排高维数组…...

GPT-4 免费体验方法

POE 在Quora上非常受欢迎的手机聊天机器人Poe App已经集成ChatGPT助手&#xff01;除了最初集成的三个聊天机器人Sage、Claude和Dragonfly外&#xff0c;Poe现在还加入了第四位ChatGPT。由于使用了ChatGPT API&#xff0c;因此Poe拥有真正的ChatGPT。 现在更是第一批集成了GP…...

中断-屏蔽位

1.中断控制器(PIC:适用于单处理器、APIC) 1.定义 中断控制器可以看作是中断服务的代理,外设五花八门,如果没有一个中断的代理,外设想要给cpu发送中断信号来处理中断。那么只能是外设连接在cpu引脚上,由于cpu引脚很宝贵,所以不可能拿出那么多引脚来供外设连接,所以就有…...

【洛谷P1636】 Einstein学画画

题目描述&#xff1a;Einstein 学起了画画。此人比较懒~~&#xff0c;他希望用最少的笔画画出一张画……给定一个无向图&#xff0c;包含 n 个顶点&#xff08;编号 1∼n&#xff09;&#xff0c;m 条边&#xff0c;求最少用多少笔可以画出图中所有的边。输入格式第一行两个整数…...

户外LED显示屏钢结构制作原则

户外LED显示屏在施工安装时是必须要制作固定钢结构的&#xff0c;因为户外LED显示屏工作环境相对比较恶劣&#xff0c;制作钢结构一是为了安全&#xff0c;二是为了提高防护等级。那么户外LED显示屏钢结构制作原则是什么呢&#xff1f;迈普光彩小编总结了一些分享个大家。 户外…...

【内网穿透】使用Haproxy反向代理搭建企业私有云:神卓互联教程

神卓互联是一款强大的内网穿透工具&#xff0c;可以帮助企业搭建私有云&#xff0c;实现对内部资源的远程访问。在搭建私有云的过程中&#xff0c;使用HAProxy反向代理可以提高系统的性能和可靠性。本文将介绍如何使用神卓互联和HAProxy反向代理搭建私有云。 步骤如下&#xf…...

spring boot项目:实现与数据库的连接

步骤【写在前面】定义数据库连接信息&#xff1a;引入数据库驱动&#xff1a;创建数据源&#xff1a;创建JdbcTemplate&#xff1a;编写DAO层&#xff1a;使用Service注解标注Service层&#xff1a;使用RestController注解标注Controller层&#xff1a;示例代码&#xff1a;app…...

【gitlab部署】centos8安装gitlab(搭建属于自己的代码服务器)

这里写目录标题部署篇序言要求检查系统是否安装OpenSSH防火墙问题准备gitlab.rb 配置坑点一忘记root密码重置使用篇gitlab转换成中文git关闭注册入口创建用户部署篇 序言 在团队开发过程中&#xff0c;想要拥有高效的开发效率&#xff0c;选择一个好的代码开发工具是必不可少的…...

2021年全国职业院校技能大赛(中职组)网络安全竞赛第三套试题A模块解析(超级详细)

2021年全国职业院校技能大赛(中职组) 网络安全竞赛试题 (3) (总分100分) 赛题说明 一、竞赛项目简介 “网络安全”竞赛共分A. 基础设施设置与安全加固;B. 网络安全事件响应、数字取证调查和应用安全;C. CTF夺旗-攻击;D. CTF夺旗-防御等四个模块。根据比赛实际情况…...

Hbase异步复制和同步复制解析

背景 Hbase是一个KV数据库&#xff0c;自然和Mysql以及Redis等会涉及到复制的问题&#xff0c;也有主从集群的概念&#xff0c;那么本文就来看下Hbase的复制逻辑 Hbase复制实现 首先我们先在回顾下&#xff0c;在Hbase实现中&#xff0c;每个RegionServer上面会包含多个Regi…...

TIKTOK海外直播公会如何申

在“清朗行动”的规范化整治下&#xff0c;国内秀场直播俨然成为了“夕阳行业”&#xff0c;早已度过了野蛮生长的阶段。随着直播公会内卷竞争加剧&#xff0c;公会的生存也愈发艰难&#xff0c;有的娱乐主播甚至纷纷转行做起了电商&#xff0c;可见国内娱乐直播行业的惨淡。 …...

6.springcloud微服务架构搭建 之 《springboot集成Gateway》

5.springcloud微服务架构搭建 之 《springboot集成Hystrix》 目录 1.gateway介绍 2.项目引入gateway 3.yml配置gateway参数 5.自定义全局Filter 6.测试 1.gateway介绍 服务网关&#xff08;Spring Cloud Gateway&#xff09;是Spring Cloud官方推出的 第二代网关框架&#…...

[N1CTF 2018]eating_cms_

目录 信息收集 代码审计 parse_url解析漏洞 信息收集 进入即是登录页面&#xff0c;抓包一看应该是SQL注入&#xff0c;但是空格、%、|等等啥的都被waf了,不太好注入&#xff0c;先信息收集一波 花一分钟扫下目录&#xff0c;发现一个viminfo和register.php Viminfo文件…...

《Spring系列》第13章 Aop切面(二) 代理创建

前言 本篇文章主要介绍AOP的源码&#xff0c;要想看懂AOP&#xff0c;那么就看AOP给容器中注入了什么组件&#xff0c;这个组件什么时候工作&#xff0c;这个组件的功能是什么&#xff1f; EnableAspectJAutoProxy会向IOC容器中注入一个后置处理器&#xff0c;它会在Bean的创…...

算法-贪心

贪心算法1信息学竞赛课堂贪心算法2贪心法实际生活中&#xff0c;经常需要求一些问题的“可行解”和“最优解”&#xff0c;这就是所谓的“最优化”问题。一般来说&#xff0c;每个最优化问题都包含一组“限制条件”和一个“目标函数”&#xff0c;符合限制条件的问题求解方案称…...

【数据结构与算法】树(Tree)【详解】

文章目录前言树一、树的基本概念1、树的定义2、基本术语3、树的性质二、树的存储结构1、双亲表示法2、孩子表示法3、孩子兄弟表示法二叉树一、二叉树的概念1、二叉树的定义2、几个特殊的二叉树3、二叉树的性质4、二叉树的存储结构二、遍历二叉树1、先序遍历2、中序遍历3、后序遍…...

OSPF------LSA 详解

LSA头部 [r1]display ospf lsdb 链路状态老化时间&#xff08;Link-State Age&#xff09; 16bits,单位s当该LSA被始发路由器产生时&#xff0c;该值被设置为0&#xff0c;之后随着该LSA在网络中被洪泛&#xff0c;老化时间逐渐累积。&#xff08;但是不能让它一直增长&#x…...

js加解密入门

首先&#xff0c;让我们简单介绍一下百度公司的文心一言。文心一言是百度公司推出的一项文本生成工具&#xff0c;它可以根据给定的主题或关键词生成一句简短而富有启发性的文字。在我们的加密和解密方法中&#xff0c;我们将利用文心一言的特点来生成随机的字符串&#xff0c;…...

vue+Echarts导入自定义地图

在vue项目先安装echarts //在vue文件中的<script>中引入 import * as echarts from "echarts"; import geoJson from ../assets/map/Fmap.json; //自定义地图的位置 import * as topojson from "topojson-client"; //使用组件topojson-client自定…...

网络六边形受到攻击

大家读完觉得有帮助记得关注和点赞&#xff01;&#xff01;&#xff01; 抽象 现代智能交通系统 &#xff08;ITS&#xff09; 的一个关键要求是能够以安全、可靠和匿名的方式从互联车辆和移动设备收集地理参考数据。Nexagon 协议建立在 IETF 定位器/ID 分离协议 &#xff08;…...

idea大量爆红问题解决

问题描述 在学习和工作中&#xff0c;idea是程序员不可缺少的一个工具&#xff0c;但是突然在有些时候就会出现大量爆红的问题&#xff0c;发现无法跳转&#xff0c;无论是关机重启或者是替换root都无法解决 就是如上所展示的问题&#xff0c;但是程序依然可以启动。 问题解决…...

如何将联系人从 iPhone 转移到 Android

从 iPhone 换到 Android 手机时&#xff0c;你可能需要保留重要的数据&#xff0c;例如通讯录。好在&#xff0c;将通讯录从 iPhone 转移到 Android 手机非常简单&#xff0c;你可以从本文中学习 6 种可靠的方法&#xff0c;确保随时保持连接&#xff0c;不错过任何信息。 第 1…...

涂鸦T5AI手搓语音、emoji、otto机器人从入门到实战

“&#x1f916;手搓TuyaAI语音指令 &#x1f60d;秒变表情包大师&#xff0c;让萌系Otto机器人&#x1f525;玩出智能新花样&#xff01;开整&#xff01;” &#x1f916; Otto机器人 → 直接点明主体 手搓TuyaAI语音 → 强调 自主编程/自定义 语音控制&#xff08;TuyaAI…...

MySQL中【正则表达式】用法

MySQL 中正则表达式通过 REGEXP 或 RLIKE 操作符实现&#xff08;两者等价&#xff09;&#xff0c;用于在 WHERE 子句中进行复杂的字符串模式匹配。以下是核心用法和示例&#xff1a; 一、基础语法 SELECT column_name FROM table_name WHERE column_name REGEXP pattern; …...

SpringCloudGateway 自定义局部过滤器

场景&#xff1a; 将所有请求转化为同一路径请求&#xff08;方便穿网配置&#xff09;在请求头内标识原来路径&#xff0c;然后在将请求分发给不同服务 AllToOneGatewayFilterFactory import lombok.Getter; import lombok.Setter; import lombok.extern.slf4j.Slf4j; impor…...

CMake控制VS2022项目文件分组

我们可以通过 CMake 控制源文件的组织结构,使它们在 VS 解决方案资源管理器中以“组”(Filter)的形式进行分类展示。 🎯 目标 通过 CMake 脚本将 .cpp、.h 等源文件分组显示在 Visual Studio 2022 的解决方案资源管理器中。 ✅ 支持的方法汇总(共4种) 方法描述是否推荐…...

A2A JS SDK 完整教程:快速入门指南

目录 什么是 A2A JS SDK?A2A JS 安装与设置A2A JS 核心概念创建你的第一个 A2A JS 代理A2A JS 服务端开发A2A JS 客户端使用A2A JS 高级特性A2A JS 最佳实践A2A JS 故障排除 什么是 A2A JS SDK? A2A JS SDK 是一个专为 JavaScript/TypeScript 开发者设计的强大库&#xff…...

[免费]微信小程序问卷调查系统(SpringBoot后端+Vue管理端)【论文+源码+SQL脚本】

大家好&#xff0c;我是java1234_小锋老师&#xff0c;看到一个不错的微信小程序问卷调查系统(SpringBoot后端Vue管理端)【论文源码SQL脚本】&#xff0c;分享下哈。 项目视频演示 【免费】微信小程序问卷调查系统(SpringBoot后端Vue管理端) Java毕业设计_哔哩哔哩_bilibili 项…...

Razor编程中@Html的方法使用大全

文章目录 1. 基础HTML辅助方法1.1 Html.ActionLink()1.2 Html.RouteLink()1.3 Html.Display() / Html.DisplayFor()1.4 Html.Editor() / Html.EditorFor()1.5 Html.Label() / Html.LabelFor()1.6 Html.TextBox() / Html.TextBoxFor() 2. 表单相关辅助方法2.1 Html.BeginForm() …...