目录打散-hash算法
前几篇说了文件上传,都是上传到了WebRoot下的up目录,这样是不行的,文件多了性能就不行了。文件一般都是分目录存放的,这里讲建目录的一种算法。
先看结果,经过本算法建的目录,结构是这样的,还以up目录为例,新建的目录都建在up目录下:
WebRoot
--up
--目录1
--子目录1
--子目录2
--子目录3
--...
--子目录16
--目录2
--目录3
--...
--目录16
说明:
1、本算法是,根据【文件名】进行哈希计算,最多只会创建16个目录,你需要做的是 把你上传的文件保存到本文件名计算出来的目录下。
2、算法只会根据文件名计算出对应的目录是16个中的某一个,而不会自动创建一级、二级、三级目录来,这是你的事。
3、一个目录不够用,一般来说,二级目录已经够了。这样目录总数是16*16是256个,如果一个目录存1000张图片的话,已经能存25万张图片了,不少了。
具体算法:
一:获取文件名的hashCode,例:
String name = "a.jpg";
int a = name.hashCode();//91057364
二:将hashCode转换成二进制:
//101011011010110110011010100 一共27位
String bin = Integer.toBinaryString(a);
//java 的int应该是4个字节,一个字节8位,一共32位。
// 但是例如a的hashCode对应的就是1100001(97)不足32位,需要补全位数为32位:
三:补全位数为32位:
//加上32个0,然后截取后32位
bin = "0000000000000000000000000000000"+bin;
bin =bin.substring(bin.length()-32);
//a.jpg 计算后:00000101011011010110110011010100
四:取最后一位
// 和 0xf 16进制的15 二进制是1111,做与运算
// 0000 0000 0000 0000 0000 0000 0000 1111
&
0000 0101 0110 1101 0110 1100 1101 0100
结果 0000 0000 0000 0000 0000 0000 0000 0100 int b = a & 0xf; //4 格式化为32位,好似没啥用:
bin = Integer.toBinaryString(b);
bin = "0000000000000000000000000000000"+bin;
bin =bin.substring(bin.length()-32);
五:转换为16进制:
String hex = Integer.toHexString(b);
System.err.println("第一层目录是:"+hex); --> 第一层目录是:4 这样第一层目录就计算出来了,第二层目录,如果还用最后4位计算,会和第一层一样,可以用倒数5-8位计算:
六:计算第二级目录
//将a 右移4位,高位补0,相当于5-8位变成了最末尾四位,取的是 1101 ,继续和0xf 与计算,其余同计算一级目录一样。
// 0000 0000 0000 0000 0000 0000 0000 1111
&
0000 0000 0101 0110 1101 0110 1100 1101
结果 0000 0000 0000 0000 0000 0000 0000 1101 int c = (a>>4) & 0xf; bin = Integer.toBinaryString(c);
bin = "0000000000000000000000000000000"+bin;
bin =bin.substring(bin.length()-32); hex = Integer.toHexString(c);
System.err.println("第二层的目录是:"+hex); -->第二层的目录是:d
这样就形成了/up/4/d目录,然后将本文件或图片放在这个目录下。
说明:0xf 是十六进制的15,转换成二进制是1111,和任何4位二进制进行&运算后,最大值也还是1111,最小值是0,所以最多只能创建16个目录。
测试的java代码:
@Test
public void temp(){
String name = "a.jpg";
int a = name.hashCode();
System.err.println(a);//91057364
//转成二进制1000001111111100001001010
String bin = Integer.toBinaryString(a);
//位数 32位
bin = "0000000000000000000000000000000"+bin;
bin =bin.substring(bin.length()-32);
System.err.println(bin+","+bin.length()); //0xf表示16进制的15,二进制是1111,& a 可以取a的最后一位 1010
//
int b = a & 0xf;
System.err.println(b);//
bin = Integer.toBinaryString(b);//4的二进制 0100
System.out.println(Integer.toHexString(b));
bin = "0000000000000000000000000000000"+bin;
//格式化为 00000000000000000000000000001010
bin =bin.substring(bin.length()-32);
System.err.println(bin+","+bin.length()); //转换成16进制
String hex = Integer.toHexString(b);
System.err.println("第一层目录是:"+hex); int c = (a>>4) & 0xf;
System.err.println(c);
bin = Integer.toBinaryString(c);
bin = "0000000000000000000000000000000"+bin;
bin =bin.substring(bin.length()-32);
System.err.println(bin+","+bin.length()); hex = Integer.toHexString(c);
System.err.println("第二层的目录是:"+hex);
}
结果L:
91057364
00000101011011010110110011010100,32
4
00000000000000000000000000000100,32
第一层目录是:4
13
00000000000000000000000000001101,32
第二层的目录是:d
4
结合文件上传Servlet应用,用的还是fileupload组件:
package com.lhy.upload; import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.List;
import java.util.UUID;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
/**
* 处理目录打散。
* 思想:对新生成的文件名进行二进制运算。
* 先取后一位 int x = hashcode & 0xf;
* 再取后第二位:int y = (hashCode >> 4) & 0xf;
* @author wangjianme
*
*/
@WebServlet(name="DirServlet",urlPatterns="/DirServlet")
public class DirServlet extends HttpServlet {
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
//设置编码
request.setCharacterEncoding("UTF-8");
//项目路径
String path = getServletContext().getRealPath("/up");
//第一步声明diskfileitemfactory工厂类,用于在指的磁盘上设置一个临时目录
DiskFileItemFactory disk = new DiskFileItemFactory();
disk.setRepository(new File("D:/tmp"));
//第二步:声明ServletFileUpoload,接收上面的临时目录
ServletFileUpload up = new ServletFileUpload(disk);
try {
//解析request
List<FileItem> list = up.parseRequest(request);
for (FileItem file : list) {
if(!file.isFormField()){
//不是普通表单域
String fileName = file.getName();
//如果是 C:\\xxx\\a.jpg格式,截取,不是不影响
fileName = fileName.substring(fileName.lastIndexOf("\\")+1);
String extName = fileName.substring((fileName.indexOf(".")));//后缀 .jpg
String newName = UUID.randomUUID().toString().replace("-", "")+extName;
//第一步:获取新名称的hashcode
int code = newName.hashCode();
//第二步:获取后一位做为第一层目录
String dir1 = Integer.toHexString(code & 0xf);
System.err.println("第一层目录: "+dir1);
//获取第二层的目录
String dir2 = Integer.toHexString((code >> 4) & 0xf);
System.err.println("第二层目录: "+dir2);
String savePath = dir1+"/"+dir2;
//组成保存的目录
savePath = path+"/"+savePath;
//判断目录是否存在
File f = new File(savePath);
if(!f.exists()){
f.mkdirs(); //创建目录
}
//保存文件
file.write(new File(savePath+"/"+newName));
System.err.println("文件保存位置:\n"+savePath+"/"+newName);
//删除临时文件
file.delete(); //带路径保存到request
request.setAttribute("fileName",dir1+"/"+dir2+"/"+newName);
}
}
request.getRequestDispatcher("/jsps/show.jsp").forward(request, response);
} catch (Exception e) {
e.printStackTrace();
}
} }
打印的信息:
第一层目录: f
第二层目录: d
文件保存位置:
D:\zhcw\apache-tomcat-7.0.69\webapps\Upload\up/f/d/826c14e14eed4eb895fac3b8335befa5.jpg
form表单:
<form action="<c:url value='/DirServlet'/>" method="post"
enctype="multipart/form-data">
你的图片:<input type="file" name="img"><br />
<input type="submit" />
</form>
shows.jsp:
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>My JSP 'index.jsp' starting page</title>
<meta http-equiv="pragma" content="no-cache">
<meta http-equiv="cache-control" content="no-cache">
<meta http-equiv="expires" content="0">
<meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
<meta http-equiv="description" content="This is my page">
</head>
<body>
<%-- <p>以下是你上传的文件</p>
<c:forEach items="${ups}" var="mm">
文件名:${mm.fileName}<br/>
类型:${mm.fileType}<br/>
大小:${mm.size}(bytes)
<hr/>
</c:forEach>
--%>
<hr color="blue"/>
<p>你上传的图片是:</p>
<img src="<c:url value='/up/${fileName}'/>"/>
</body>
</html>
上传:
服务器up目录:
z
展示页show.jsp:
多上传几张图片,可以看到会在up目录建目录:
上传的文件都被保存到了各自经过哈希计算后的目录,因为图片重命名是uuid是随机唯一的,所以被存放到那个目录不能确定。会被随机存放到生成的16个文件夹下。
目录打散-hash算法的更多相关文章
- 一致性 hash 算法(转)
add by zhj:介绍了什么是一致性hash,以及实现一致性hash的一种算法. 原文:http://my.oschina.net/u/195065/blog/193614 目录[-] 一致性 h ...
- hash算法打散存储文件
1.首先,为防止一个目录下面出现太多文件,所以使用hash算法打散存储 举例代码: int hashcode = filename.hashCode();//得到hashCode int dir1 = ...
- 【Java EE 学习 22 上】【文件上传】【目录打散】【文件重命名】
1.文件上传概述 (1)使用<input type="file">的方式来声明一个文件域. (2)表单提交方式一定要是post方式才行 (3)表单属性enctype 默 ...
- Nginx+Memcache+一致性hash算法 实现页面分布式缓存(转)
网站响应速度优化包括集群架构中很多方面的瓶颈因素,这里所说的将页面静态化.实现分布式高速缓存就是其中的一个很好的解决方案... 1)先来看看Nginx负载均衡 Nginx负载均衡依赖自带的 ngx_h ...
- 分布式缓存设计:一致性Hash算法
缓存作为数据库前的一道屏障,它的可用性与缓存命中率都会直接影响到数据库,所以除了配置主从保证高可用之外还需要设计分布式缓存来扩充缓存的容量,将数据分布在多台机器上如果有一台不可用了对整体影响也比较小. ...
- Android NDK JNI 入门笔记-day04-NDK实现Hash算法
* Android NDK JNI 入门笔记目录 * 开头 前面的学习,我们已经掌握了 NDK 开发的必备知识. 下一步就要多实践,通过创造问题并解决问题,来增加熟练度,提升经验. 日常开发中,经常会 ...
- Hash 算法与 Manacher 算法
目录 前言 简单介绍 简述 Hash 冲突 离散化 基本结构 普通 Hash 简述 例题 字符串 Hash 简单介绍 核心思想 基本运算 二维字符串 Hash 例题 兔子与兔子 回文子串的最大长度 后 ...
- 编程艺术第十六~第二十章:全排列/跳台阶/奇偶调序,及一致性Hash算法
目录(?)[+] 第十六~第二十章:全排列,跳台阶,奇偶排序,第一个只出现一次等问题 作者:July.2011.10.16.出处:http://blog.csdn.net/v_JULY_v. 引言 ...
- 对一致性Hash算法,Java代码实现的深入研究
一致性Hash算法 关于一致性Hash算法,在我之前的博文中已经有多次提到了,MemCache超详细解读一文中"一致性Hash算法"部分,对于为什么要使用一致性Hash算法.一致性 ...
随机推荐
- Linux抓包
默认系统里边没有安装有tcpdump的,无法直接使用 这里我们可以使用yum来直接安装它 yum install -y tcpdump 如果忘记了这个软件的用法,我们可以使用 tcpdump ...
- C++之类和对象的使用(三)
对象数组 如果构造函数只有一个参数,在定义数组时可以直接在等号后面的花括号内提供.Student stud[3]={90,92,01};//合法 如果构造函数有多个参数,则不能用在定义时直接所提供所有 ...
- CentOS7下安装配置Nginx
一:安装依赖项 1.pcre:2.openssl:3.zlib:4.gcc:可直接通过yum安装 二:创建nginx账户(可以配置nginx.conf的user为此账户) useradd nginx ...
- 【redis】linux上的安装与配置(详细图解)
转载自:https://blog.csdn.net/yjqyyjw/article/details/73293455:经过个人测试也适用于当前最新稳定的3.x的版本,顺便填了几个坑. 1.下载 htt ...
- cxf-rs 和 swagger 的点
1. 从web.xml 说起 <servlet> <servlet-name>CXFServlet</servlet-name> <servlet-class ...
- docker 命令介绍
查看镜像 docker images: 列出imagesdocker images -a :列出所有的images(包含历史)docker images --tree :显示镜像的所有层(layer) ...
- AOT和JIT以及混合编译的区别、优劣
AOT,JIT是什么? JIT,即Just-in-time,动态(即时)编译,边运行边编译: AOT,Ahead Of Time,指运行前编译,是两种程序的编译方式 区别 这两种编译方式的主要区别在于 ...
- Android DalivkVM与JVM的比较
JVM 与 DalivkVM的区别 Android 为什么还有搞一个Dalivk虚拟机,不是已经就有Java虚拟机了吗,为什么还要专门搞一个Dalivk虚拟机呢? 答: 1.以前Java是Sun公司的 ...
- CSS 温故而知新
如何让文字垂直居中 需要设置div的height,line-height 为一样的值,如下所示: <div class="ui-bar ui-bar-e" style=&qu ...
- 再也不用线上倒数据了,使用 Faker 来造一批假的数据吧。
背景每当建表之后,常常需要写一批假的数据,用于测试算法.数据量的压力测试.列表翻页. 查看详情.数据关联等.这时就需要借助一款造数据的工具,它就是今天所要介绍的 Faker. 介绍 Faker 这个工 ...