线程数据共享和安全 -ThreadLocal-----Java web文件上传下载应该注意什么
ThreadLocal
- 1、ThreadLocal 可以实现在同一个线程数据共享, 从而解决多线程数据安全问题。
- 2、ThreadLocal 可以给当前线程关联一个数据(普通变量、对象、数组)set 方法设置 关联数据。
public void set(T value) { //获取当前线程 * Thread t = Thread.currentThread(); * //获取当前线程的 ThreadLocal.ThreadLocalMap 属性 threadLocals , 类型是 ThreadLocal 的静态内部类 * //threadLocals 有 一 个 属 性 Entry[], 类 型 ThreadLocal.ThreadLocalMap.Entry * //k-> ThreadLocal 对象 v-> 值 * ThreadLocalMap map = getMap(t); * if (map != null) * map.set(this, value);//存放这里的 this 就是 ThreadLocal, 可以 debug 源码,一目了然 * else * createMap(t, value);//创建 } threadLocal.set(dog);
3、ThreadLocal 可以像 Map 一样存取数据,key 为当前线程, get 方法获取当前线程的共享数据。
public T get() { * //1. 先得到当前的线程对象 * Thread t = Thread.currentThread(); * //2.通过线程获取到对应的ThrealLocalMap * ThreadLocalMap map = getMap(t); * if (map != null) { * //3. 如果map不为空, 根据当前的 threadlocal对象,得到对应的Entry * ThreadLocalMap.Entry e = map.getEntry(this); * //4. 如果e 不为null * if (e != null) { * @SuppressWarnings("unchecked") * //返回当前threadlocal关联的数据value * T result = (T)e.value; * return result; * } * } * return setInitialValue(); * } //获取ThrealLocalMap对象的Entry[] table里的当前线程对象的信息Entry(k,v)线程名和关联数据 Object o = T1.threadLocal1.get();
4、每一个 ThreadLocal 对象,只能为当前线程关联一个数据,如果不重新创建ThreadLocal对象实例,后面关联的对象会替换前面的对象。如果希望在当前线程共享多个数 据,就需要使用多个 ThreadLocal 对象实例。
threadLocal.set(dog); //threadLocal.set(pig);//会替换 dog //如果希望在同一个线程共享多个对象/数据,就在创建一个 ThreadLocal 对象 threadLocal2.set(pig);
- 5、每个 ThreadLocal 对象实例定义的时候,一般为 static 类型
- 6、ThreadLocal 中保存数据,在线程销毁后,会自动释放。
web 应用常用功能 -文件上传下载
1.文件的上传和下载,是常见的功能。
2.如果是传输大文件,一般有专门工具或者插件 。
3.文件上传下载需要使用到两个包 , 需要导入
文件上传
1.使用表单提交。
//判断是不是文件表单(enctype="multipart/form-data") if (ServletFileUpload.isMultipartContent(request)) { //是文件表单就构建创建解析上传数据的工具对象 //2. 创建 DiskFileItemFactory 对象, 用于构建一个解析上传数据的工具对象 DiskFileItemFactory diskFileItemFactory = new DiskFileItemFactory(); //3. 创建一个解析上传数据的工具对象 ServletFileUpload servletFileUpload =new ServletFileUpload(diskFileItemFactory);}
2.action提交页面路径和之前一样,浏览器会自己解析前面的斜杠。
3.请求method指定为post。
4.enctype:encodetype编码类型默认是application/x-www-urlencodede即url编码,这种编码一般适用于文本。编码不正确会出现乱码。
5.进行二进制文件提交enctype要指定multipart/form-data 表示表单提交的数据有多个部分组成,可以提交二进制数据和文本数据。文件使用io技术进行处理。
文件上传注意事项和细节
1、如果文件上传会保存到一个目录下,当上传文件很多时,会造成访问文件速度变慢,因此 可以将文件上传到不同目录 比如 一天上传的文件,统一放到一个文件夹 年月日, 比如21001010 /文件名文件夹
2、一个完美的文件上传,要考虑的因素很多,比如断点续传、控制图片大小,尺寸,分片 上传,防止恶意上传等,在项目中,可以考虑使用 WebUploader 组件(百度开发) http://fex.baidu.com/webuploader/doc/index.html
3、文件上传功能,在项目中建议有限制的使用,一般用在头像、证明、合同、产品展示等, 如果不加限制,会造成服务器空间被大量占用 [比如 b 站评论,就不能传图片,微信发 1 次朋友圈最多 9 张图等…]
4、文件上传,创建 web/upload 的文件夹,在 tomcat 启动时,没有在 out 目录下 创建 对 应的 upload 文件夹, 原因是 tomcat 对应空目录是不会在 out 下创建相应目录的,所以,只 需在 upload 目录下,放一个文件即可, 这个是 Idea + Tomcat 的问题, 实际开发不会存 在.
文件下载
读取下载的文件数据,返回给客户端/浏览器
//(1) 创建一个和要下载的文件,关联的输入流 InputStream resourceAsStream = servletContext.getResourceAsStream(downLoadFileFullPath); //(2) 得到返回数据的输出流 [因为返回文件大多数是二进制(字节), IO java基础] ServletOutputStream outputStream = response.getOutputStream(); //(3) 使用工具类,将输入流关联的文件,对拷到输出流,并返回给客户端/浏览器 IOUtils.copy(resourceAsStream, outputStream);
文件下载注意事项和细节
- 1.文件下载,比较麻烦的就是文件名中有中文,需要进行处理。如果不处理编码就会出现乱码。设置编码后,浏览器得到文件名会使用相应方法进行解码,中文文件名就不会出现乱码。
// 获取到要下载的文件的名字 request.setCharacterEncoding("utf-8"); String downLoadFileName =request.getParameter("name");
2.不同的浏览器写法不一样,因此在代码中,我们要针对不同浏览器进行编码处理。
//(1)如果是Firefox 则中文编码需要 base64 //(2)Content-Disposition 是指定下载的数据的展示形式 , 如果attachment 则使用文件下载方式 //(3)如果是其他(主流ie/chrome) 中文编码使用URL编码 if (request.getHeader("User-Agent").contains("Firefox")) { // 火狐 Base64编码 response.setHeader("Content-Disposition", "attachment; filename==?UTF-8?B?" + new BASE64Encoder().encode(downLoadFileName.getBytes("UTF-8")) + "?="); } else { // 其他(主流ie/chrome)使用URL编码操作 response.setHeader("Content-Disposition", "attachment; filename=" + URLEncoder.encode(downLoadFileName, "UTF-8")); }
- 3.对于网站的文件,很多文件使用另存为即可下载,对于大文件(文档,视频),会使用专 业的下载工具(迅雷、百度,腾讯,华为网盘等)。
- 4.对于不同的浏览器, 在把文件下载完毕后,处理的方式不一样, 有些是直接打开文件,有些是将文件下载到 本地/下载目录。