首页 > 代码库 > 目录打散-hash算法
目录打散-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 //00000101011011010110110011010100 int b = a & 0xf; System.err.println(b);//4 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=http://www.mamicode.com/‘/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=http://www.mamicode.com/‘/up/${fileName}‘/>"/> </body></html>
上传:
服务器up目录:
z
展示页show.jsp:
多上传几张图片,可以看到会在up目录建目录:
上传的文件都被保存到了各自经过哈希计算后的目录,因为图片重命名是uuid是随机唯一的,所以被存放到那个目录不能确定。会被随机存放到生成的16个文件夹下。
目录打散-hash算法