Struts2_3_国际化处理_自定义拦截器_文件上传及下载_OGNL

Struts2国际化处理

浏览器根据当前的语言环境自动查找对应的语言环境资源包, 使jsp显示合适的语言数据环境
Struts2实现国际化, 动作类必须继承ActionSupport

  1. 创建资源包
资源包由多个properties文件组成, properties文件的文件名必须满足命名规范: 文件名_语言代码_国家代码.properties
语言代码/国家代码由iso规定
例: ms_zh_CN.properties
注:当properties文件命名为: 文件名.properties表名它为默认的资源包, 当浏览器没有找到对应的语言资源包的时候, 就会使用默认的资源包
  1. 配置资源包
1. 配置全局资源包覆盖default.properties文件中的常量struts.custom.i18n.resources例:<constant name="struts.custom.i18n.resources" value="com.test.message"></constant><package name="p1" extends="struts-default"><action name="action1" class="com.action.DemoAction"></action></package>com.test.message代表资源包所在的位置
2. 配置包范围的资源包命名规范: package_语言代码_国家代码.properties(固定命名格式)使用这种命名规范, 资源包只能被该包或子包的动作类访问
3. 局部资源包命名规范: 动作名称_语言代码_国家代码.properties只为动作类服务
注:资源包的搜索顺序, 满足就近原则: 局部 > 包 > 全局
  1. 在jsp中读取资源包中内容
借助struts2的标签进行读取
例:<%@ taglib uri="/struts-tags" prefix="s"%><s:text name="key"></s:text>
显示的数据对应的是properties文件中名为key的值, 此时输出的内容是全局资源包中的内容
原因: 没有经过动作类, 以及包, 直接搜索的是全局的资源包
当经过动作类的时候, 就是按照搜素顺序进行搜索资源包
  1. 获取指定的资源包
使用<s:i18n name="com.test.message">
直接获取message下面的properties文件

Struts2中的拦截器

Struts2中很多功能都是拦截器完成的, 并且都是AOP思想的一种体现

自定义拦截器

继承AbstractInterceptor类或实现Interceptor接口, 重写intercept方法
配置拦截器, 拦截器只有在配置的条件下才能使用

例如:

public class Demo1Interceptor extends AbstractInterceptor{public String intercept(ActionInvocation invocation) throws Exception{return null;	}
}
//继承AbstractInterceptor重写intercept方法
声明拦截器:
struts.xml:
<interceptors><interceptor name="demo1Interceptor" class="com.interceptors.Demo1Interceptor" />
</interceptors>
使用拦截器:
<action name="action1" class="com.interceptors.Demo1Interceptor" method="save"><interceptor-ref name="demo1Interceptor"></interceptor-ref><result>/success.jsp</result>
</action>
在interceptors标签中可以配置多个拦截器声明

注:
使用拦截器必须先声明然后再使用, 并且一旦配置了任何一个拦截器, 默认的拦截器就都失效了

拦截器执行顺序

下面通过例子说明:

Struts.xml:
<struts><constant name="struts.devMode" value="true"></constant><package name="p1" extends="struts-default"><!--声明拦截器列表--><interceptors><interceptor name="demo1Interceptor" class="com.interceptors.Demo1Interceptor" /></interceptors><action name="action1" class="com.interceptors.Demo1Interceptor" method="save"><!--使用拦截器--><interceptor-ref name="demo1Interceptor"></interceptor-ref><result>/success.jsp</result></action></package>
</struts>
在拦截器类中的ntercept方法有如下操作:
public String intercept(ActionInvocation invocation) throws Exception{System.out.println("拦截器执行, 访问动作方法之前");String result = invocation.invoke();System.out.println("拦截器执行, 访问动作方法后");return result;
}Action类:
public class Demo1Action extends ActionSupport{public String save(){System.out.println("动作方法save执行");return SUCCESS;}
}success.jsp:
<body><%System.out.println("success.jsp执行了")%>
</body>
当访问save动作的时候有如下的运行顺序:拦截器执行了, 分文动作之前动作方法save执行success.jsp执行了拦截器执行了, 访问动作方法之后

从上面的运行顺序可以得出一下结论:
1.访问动作方法之前一定会先执行所有的拦截操作
2.拦截操作执行完,必须使用invocation的invoke方法做放行操作, 此处的操作类似于Filter中的doFilter操作
3.当动作类, 以及结果视图中的内容执行完之后又重新反向将拦截器都执行一遍
此处请看一下的Struts2的效果执行图:
Struts2的核心结构图
其实通过Struts2的执行图就可以看出, 多个拦截器的运行过程, 在使用多个拦截器的时候, 它与单个拦截器的写法一样

intercept方法的返回值

正如上面例子看到的一样, intercept方法具有一个String类型的返回值
String result = invocation.invoke();
此时的result就是代表结果视图, 代表动作方法的返回值

解决自定义拦截器条件下, 默认拦截器出现无效的情况

正如前面提到的, 当我们使用自定义的拦截器的时候出现的问题就是, 默认系统提供的拦截器都会无效, 导致demo各种报错
所以现在需要做的就是将Struts2提供的默认的拦截器加载到我们的配置文件中
有如下的解决方案:

  1. 把默认的拦截器加入到配置文件中
将默认的拦截器加入到配置文件, 需要做的就是在使用自定义拦截器之前, 在使用自定义的拦截器之前多加一个系统默认的interceptor-ref使用
这里要注意的是, 系统默认的拦截器不需要做声明操作, 只需要在action标签中做修改
例:<action name="action1" class="com.interceptors.Demo1Interceptor" method="save"><!--使用系统默认的拦截器--><interceptor-ref name="defaultStack"></interceptor-ref><!--使用自定义的拦截器--><interceptor-ref name="demo1Interceptor"></interceptor-ref><result>/success.jsp</result></action>

但是上面的操作会暴露出一个问题: 就是每当我需要添加一个自定义的拦截器的时候都需要使用系统默认的拦截器, 重复代码过多, 所以下面采取的措施就是, 将每个action中的拦截器抽取出来, 统一使用一个新的package管理, 然后在触发action的时候使用拦截器
例如:

新建一个package
<package name="mydefault" extends="struts-default"'><interceptors><!--声明自定义的拦截器--><interceptor name="demo1Interceptor" class="com.interceptors.Demo1Interceptor">	</interceptor><!--声明拦截器栈--><interceptor-stack name="myDefaultStack"><!--定义使用拦截器--><interceptor-ref name="defaultStack"></interceptor-ref><interceptor-ref name="demo1Interceptor"></interceptor-ref></interceptor-stack></interceptors><!--覆盖默认拦截器定义使用参数, 使用我们自定义的拦截器栈--><default-interceptor-ref name="myDefaultStack"></default-interceptor-ref> 
</package>
注: default-Interceptor-ref是struts-default中的标签, 它包含了struts2默认的所有拦截器定义使用(ref)
<package name="p2" extends="mydefault"><action name="A" class="com.test.action.Demo1Action" method="A"><result>/1.jsp</result>	</action><action name="B" class="com.test.action.Demo1Action" method="B"><result>/2.jsp</result>	</action>
</package>

经过上面的修改之后, 在访问动作A, B就不在需要进行拦截器的定义声明操作, 但是由此也暴露出一个问题, 就是当访问package p2下面的所有动作, 都将会触发拦截器, 如果有的动作我们不想触发拦截器, 那么就出现问题, 所以针对上面的问题, 有下面的解决方案
2. 继承AbstractInterceptor的抽象子类MethodFilterInterceptor
MethodFilterInterceptor将AbstractInterceptor的抽象方法intercept重写, 但是具有一个新的抽象方法doIntercept(ActionInvocation invocation);
MethodFilterInterceptor中具有两个属性:
Set< String> excludeMethods //哪些动作不需要拦截
Set< String> includeMethods //哪些动作需要拦截
上面的两个属性可以做注入操作, 通过配置struts.xml的方式做到配置拦截操作
所以在自定义拦截器的时候, extends MethodFilterInterceptor类, 实现doIntercept(ActionInvocation invocation);

//Interceptor中写法
public class CheckLogin2Interceptor extends MethodFilterInterceptor{protected String doIntercept(ActionInvocation invocation) throws Exception{HttpSession session = ServletActionContext.getRequest().getSession();Objective obj = session.getAttribute("user");if (obj != null){String result = invocation.invoke();return result;	}else{return "error";	}}
}
//struts.xml写法
<package name="mydefault" extends="struts-default"'><interceptors><interceptor name="checkLogin2Interceptor" class="com.interceptors.CheckLogin2Interceptor"></interceptor><interceptor-stack name="myDefaultStack"><interceptor-ref name="defaultStack"></interceptor-ref><interceptor-ref name="checkLogin2Interceptor"><!--对A方法不拦截--><param name="excludeMethods">A</param></interceptor-ref></interceptor-stack></interceptors><default-interceptor-ref name="myDefaultStack"></default-interceptor-ref> 
</package>
<package name="p2" extends="mydefault"><action name="A" class="com.test.action.Demo1Action" method="A"><result>/1.jsp</result>	</action><action name="B" class="com.test.action.Demo1Action" method="B"><result>/2.jsp</result>	</action>
</package>

注: 当我们需要进行方法拦截的时候, 就可以通过注入的方式, 进行指定拦截, 但是也暴露了一个问题, 如果只有在确定动作类, 动作方法之后才能确定param中的参数
所以针对这个问题, 有下面的解决方案:

我们不在拦截器定义使用的时候, 而是在触发action的时候对特定的方法使用特定的拦截器
上面struts.xml有如下的修改:
<package name="mydefault" extends="struts-default"'><interceptors><interceptor name="checkLogin2Interceptor" class="com.interceptors.CheckLogin2Interceptor"></interceptor><interceptor-stack name="myDefaultStack"><interceptor-ref name="defaultStack"></interceptor-ref><!--此处不配置param参数--><interceptor-ref name="checkLogin2Interceptor"></interceptor-ref></interceptor-stack></interceptors><!--自定义默认拦截器栈--><default-interceptor-ref name="myDefaultStack"></default-interceptor-ref> 
</package>
<package name="p2" extends="mydefault"><action name="A" class="com.test.action.Demo1Action" method="A"><!--在action中配置interceptor--><interceptor-ref name="myDefaultStack"><!--对自定义的checkLogin2Interceptor拦截器中的excludeMethods, 进行参数注入操作, 在触发A动作的时候, 不拦截A操作--><param name="checkLogin2Interceptor.excludeMethods">A</param></interceptor-ref><result>/1.jsp</result></action><action name="B" class="com.test.action.Demo1Action" method="B"><result>/2.jsp</result>	</action>
</package>

拦截器类图
// 从网上找的
拦截器类图, 描述各拦截器之间的关系

Struts2的文件上传与下载

文件上传

与Servlet中的文件上传一样, Struts2中的文件上传必须明确:
1.表单method为post
2.enctype取值必须是multipart/form-data
3.提供文件选择域
下面使用Struts2提供的标签进行演示:

<s:form action="upload" enctype="multipart/form-data"><s:textfield name="username" lable="用户名"></s:textfield><s:file name="photo" lable="照片"></s:file><s:submit value="提交"></s:submit>
<s:form>

注: Struts2中使用file标签进行文件上传, Struts2文件上传的底层实现还是commons-fileupload和commons-io
动作类:

public class UploadAction extends ActionSupport{private String username;private File photo; //上传字段private String photoFileName; //上传文件名, 固定写法, 上传字段名称+FileName(区分大小写)private String photoContentType; //上传文件的MIME类型, 固定写法, 上传文件名+ContentType(区分大小写)//在上传文件后, struts会自动对这两个属性做注入处理public String upload(){String 	dir = ServletActionContext.getServletContext().getRealPath("/WEB-INF/files");//获取files路径File file = new File(dir);if (!file.exists()){file.mkdirs();	}photo.renameTo(new File(File, photoFileName));//将上传的photo存入file中return null;}//下面一堆get, set方法, 我就不写了

struts.xml:

<!--覆盖默认的maxSize大小, 默认大小为2M-->
<constant name="struts.multipart.maxSize" value="5242880"></constant>
<package name="upload" extends="struts-default"><action name="upload" class="com.action.UploadAction" method="upload"><interceptor-ref name="defaultStack"><!--使用默认的fileUpload拦截器, 对extension做注入处理, 限制上传文件扩展名--><param name="fileUpload.allowedExtension">jpg.png</param><!--限制上传文件类型--><param name="fileUpload.allowedTypes">image/jpg, image/pjpeg,image/png</param></interceptor-ref><result name="input">/upload.jsp</result></action>
</package>

修改上传文件出现的错误信息提示
当上传文件的时候会出现, 文件类型/大小/后缀名…错误, 需要通过国际化的方式修改错误提示信息
有如下操作:
1.创建资源文件, 例如: fileuploadmessage.properties 放在src下
2.添加错误信息, 例如: struts.messages.error.uploading=上传错误:{0}, 其余key查阅struts2-core.jar\org.apache.struts2\struts-message.properties, 只需要将value中英文值修改即可
3.在struts.xml中使用< constant name=“struts.custom.i18n.resource” value=“com…fileuploadmessage”/>, value代表资源文件位置
多文件上传
多文件上传, 在jsp中, 只需多添加file标签, 在UploadAction中修改上传字段, 上传文件名, 上传文件类型为数组/list类型即可, struts会自动将上传的file分配到数组/list中

文件下载

文件下载是一种结果类型(Stream)

Action类:
public class DownloadAction extends ActionSupport{private InputStream fileInputStream; //此处字段名不能写in, 会报错public String download() throws Exception{String filePath = ServletActionContext.getServletContext().getRealPath("/WEB-INF/files/1.jpg");fileInStream = new FileInputStream(filePath);return SUCCESS;}//下面一堆get, set操作, 进行注入
}
struts.xml配置:
<action name="download" class="com.action.DownloadAction" method="download"><result name="success" type="stream"><!--配置inputName属性, 将action类中的输入流--><param name="inputName">fileInputStream</param><!--设置响应消息头, 告知浏览器以下载的方式打开--><param name="contentDisposition">attachment;filename=image.jpg</param><!--设置响应消息头, 告知浏览器, 响应正文的MIME类型--><param name="contentType">application/octet-stream</param></result>
</action>
注: 在写filename会出现文件名写死的情况, 所以采用OGNL表达式解决, 写成: filename=${filename}, 在action类中获取filename属性, 注入处理

OGNL

OGNL类似于EL表达式, Struts2表达式语言, 在Struts2中使用OGNL必须使用Struts2标签库
例:

<s:property value="'OGNL_Expression'">
s:property类似于EL中<%=%>操作, value代表输出的内容, 双引号中的内容就是一个OGNL表达式

在OGNL中可以调用普通方法, 静态方法, 使用静态变量, 但是在EL表达式中只能使用静态方法
调用方法的时候, 需要写出当前方法的完整路径, 并且在路径以及方法名前加"@"符号
当调用静态方法的时候, 需要开启OGNL静态访问: < constant name=“struts.ognl.allowStaticMethodAccess” value=“true”></ constant>
此时才能在OGNL中调用静态方法
例:

<s:property value="@java.lang.Math@random">

在form表单中, radio以及check中就有OGNL的应用
例:

<s:radio name="gender" list="{'男', '女'}"></s:radio>
<s:radio name="gender" list="#{'1':'男', '0':"女"}">
创建List集合使用"{}", List的value直接就是男
创建Map对象使用"#{}", 上面的'1'代表男的value, key作为标签的value

contextMap

contextMap主要用于存放每次动作访问的数据
每次执行动作之前, 核心控制器StrutsPrepareAndExecuteFilter都会创建一个ActionContext和ValueStack对象。且每次动作访问都会创建, 这两个对象存储了整个动作访问期间用到的数据。并且把数据绑定到了线程局部变量(ThreadLocal)上
动作类是多例的, 每次动作访问都会实例化, 确保线程安全
contextMap中存放的内容

1.valuestack(root) : java.util.List; list类型, 非map
2.application: java.util.Map<String, Object>; 存放ServletContext中所有属性
3.session: java.util.Map<String, Object>; 存放HttpSession中所有属性
4.request: java.util.Map<String, Object>; 存放ServletRequest中所有属性
5.parameters: java.util.Map<String, Object>; 存放各种参数
6.attr: java.util.Map<String, Object>; 存放页面, 请求, 会话, 应用范围内的所有属性

注: 除valuestack是list, 其余都是map, contextMap也是map

未完待续~~

上面有错, 还请指出, 如果认为我写的还不错, 还请点个赞, 多多支持一下, O(∩_∩)O~~

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

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

相关文章

RBAC 基于角色的访问控制

RBAC&#xff08;Role-Based Access Control&#xff0c;基于角色的访问控制&#xff09;&#xff0c;就是用户通过角色与权限进行关联。简单地说&#xff0c;一个用户拥有若干角色&#xff0c;每一个角色拥有若干权限。这样&#xff0c;就构造成“用户-角色-权限”的授权模型。…

C语言书籍大全

学习过linux的同学应该都知道&#xff0c;linux系统几乎都是用c编写&#xff0c;包括现在主流的android,ios系统&#xff0c;都是使用c编程。为了方便大家学习C语言&#xff0c;小编搜集整理了市面几乎所有的C语言书籍&#xff0c;下面给出部分比较有名的&#xff0c;在微信公众…

android 7.1 apk的systemuid相同导致问题[2]

1上周发的一篇文章《android 7.1 apk的systemuid和系统应用Setting相同导致开机找不到库的问题》&#xff0c;然后有个小伙伴找我讨论了&#xff0c;觉得自己的脑洞了打开了很多&#xff0c;所以针对这个问题再次做一次总结&#xff0c;如果大家有好的建议也可以在文末留言。2先…

送书送书送书,(包邮)送,仅此而已!

亲爱的各位fans们&#xff0c;大家好&#xff0c;这次小编联合5个公众号朋友一起送书&#xff0c;一共送出 6本&#xff0c;当然包邮&#xff01;感谢本次活动的赞助商北京大学出版社。6个公众号联合送书&#xff0c;一共6本书&#xff0c;每个公众号送1本(包邮)。 6个公众号同…

requests 获取div_爬虫系列第五篇 使用requests与BeautifulSoup爬取豆瓣图书Top250

上一篇我们学习了BeautifulSoup的基本用法&#xff0c;本节我们使用它来爬取豆瓣图书Top250。一、网页分析我们爬取的网页的url是https://book.douban.com/top250?icnindex-book250-all。首页如图与豆瓣电影Top250差不多&#xff0c;将页面拉到最底部&#xff0c;可以看到分页…

python--socket套接字/TCP

socket套接字/TCP 一 客户端/服务器架构 C/S架构&#xff0c;包括 硬件C/S架构&#xff08;打印机&#xff09;软件C/S 架构&#xff08;web服务&#xff09;C/S架构的软件&#xff08;软件属于应用层&#xff09;是基于网络进行通信的Server端要&#xff1a; 1.力求一直提供服…

uniapp封装网络请求_八张图带你走进“通过一个完美请求封装一个网络模块”

本文提供视频课程讲解&#xff0c;需要的小伙伴可以点赞私信‘’网络模块‘’前往领取学习大纲1、网络模块在应用中的地位1.1当今占统治地位的网络组件OKHttp OkHttp 是一个相对成熟的解决方案&#xff0c;据说 Android4.4 的源码中可以看到 HttpURLConnection 已经替换成 OkHt…

毕业的这0111年

1.2004年&#xff0c;不知道大家对这个时间有没有感觉&#xff0c;那几年我正在读高中&#xff0c;韩寒的《三重门》席卷校园&#xff0c;同样还有郭敬明的《夏至未至》。那时候的我&#xff0c;还挣扎在温饱阶段&#xff0c;我每天吃饭的时候都想上食堂的三楼吃风味&#xff0…

我的互助小蜜圈

写在前面 从不久之前到现在&#xff0c;我的微信公众号从一个小小小阶段到了另一个小小阶段&#xff0c;非常感谢各位读者对我的信任和支持&#xff0c;当然我也是非常用心的维护这这群大佬&#xff0c;时刻想着怎样给各位大佬做好服务 &#xff0c;比如我正在溜娃&#xff0c…

比较两个表格的不同_两表数据的核对,WPS表格似乎更加方便容易

在EXCEL中的两个表格的数据对比&#xff0c;可能使用的方法会采用到查询语句&#xff0c;VBA之类的。显得在使用的过程显得有些复杂。在不经间发现WPS表格的功能处理此类的问题显得就比较的简单的多。查看并标识出分数相同的内容如下面的分数表&#xff0c;选择分数栏的范围&am…

我的知识小密圈

写在前面从不久之前到现在&#xff0c;我的微信公众号从一个小小小阶段到了另一个小小阶段&#xff0c;非常感谢各位读者对我的信任和支持&#xff0c;当然我也是非常用心的维护这这群朋友&#xff0c;时刻想着怎样给大家做好服务 &#xff0c;比如我正在溜娃&#xff0c;隔断时…

vk_down 每次下翻丙行 c++_笔记本接口不够用?不妨试试这款Type-C拓展坞,给你7个接口用...

随着笔记本电脑越来越轻薄化&#xff0c;已经很难再布局较多的数据接口了&#xff0c;但是在办公等环境下总是需要这些接口来满足需求。就拿小新个人来说&#xff0c;使用的是小米笔记本12.5寸款的&#xff0c;该款笔记本仅有三个接口&#xff0c;分别是全功能 USB-C 接口 x 1、…

学习,才是最好的投资~

推荐语&#xff1a;我因为王小波常说而喜欢的英国哲学家罗素的一句话&#xff1a; 参差多态乃是幸福的本源。正是因为有了多种多样的行业&#xff0c;才使得我们的职业也是多种多样&#xff0c;行行出大牛&#xff01;这个世界天生就注定有人搞互联网的&#xff0c;Linux&#…

第二百四十八节,Bootstrap轮播插件

Bootstrap轮播插件 学习要点&#xff1a; 1.轮播插件 本节课我们主要学习一下 Bootstrap 中的轮播插件。 一&#xff0e;轮播 轮播插件就是将几张同等大小的大图&#xff0c;按照顺序依次播放。 基本实例。 第一步&#xff0c;给轮播器区域div设置一个id给轮播器区域div设置样…

xgboost分类_XGBoost(Extreme Gradient Boosting)

一、XGBoost在Ensemble Learning中的位置机器学习中&#xff0c;有一类算法叫集成学习&#xff08;Ensemble Learning&#xff09;&#xff0c;所谓集成学习&#xff0c;指将多个分类器的预测结果集成起来&#xff0c;作为最终预测结果&#xff0c;它要求每个分类器具备一定的“…

Android技术架构演进与未来

本文阅读大约需15分钟 引言众所周知&#xff0c;Android是谷歌开发的一款基于Linux的开源操作系统&#xff0c;每年迭代一次大版本升级。 小米、华为、OPPO、VIVO、三星等各大厂商对Android原生系统进行二次开发衍生出具有各家特色的系统&#xff08;比如MIUI&#xff09;&…

Hibernate_1_配置文件详解_基础案例_Hibernate工具类_API详解_持久化类编写规则

Hibernate( ORM框架 ) Hibernate是一个数据持久化层的ORM框架. 它通过JavaBean, 数据库中的表与自身的映射关系达到表中数据的增删改查 特性 1.对JDBC访问数据库的代码进行封装, 简化数据访问的重复性代码 2.使用反射机制完成对Bean的封装 3.轻量级框架,支持关系型数据库 核…

建筑电气工程设计常用图形和文字符号_建筑水电图纸看不懂?10年老师傅教你看图技巧,分分钟安排...

1、建筑给排水工程包括&#xff1a;给水、排水、热水、消火栓、自动喷淋等常用系统&#xff0c;其管道当中流动的是水。(其管道输送介质为水)2、给排水系统的主要功能&#xff1a;(1)建筑给水系统的任务&#xff0c;就是经济合理地将水由室外给水管网输送到装置在室内的各种配水…

Android系统架构开篇

Android系统庞大且错综复杂&#xff0c;Gityuan带领大家初探Android系统整体架构&#xff0c;一窥其全貌。一、引言本文作为Android系统架构的开篇&#xff0c;起到提纲挈领的作用&#xff0c;从系统整体架构角度概要讲解Android系统的核心技术点&#xff0c;带领大家初探Andro…

20155220 实验三 敏捷开发与XP实践 实验报告

20155220 实验三 敏捷开发与XP实践 实验报告 实验内容 XP基础XP核心实践相关工具实验要求 没有Linux基础的同学建议先学习《Linux基础入门&#xff08;新版&#xff09;》《Vim编辑器》 课程完成实验、撰写实验报告&#xff0c;实验报告以博客方式发表在博客园&#xff0c;注意…