目录打散-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=‘/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个文件夹下。

时间: 2024-07-30 16:35:48

目录打散-hash算法的相关文章

一致性 hash 算法(转)

add by zhj:介绍了什么是一致性hash,以及实现一致性hash的一种算法. 原文:http://my.oschina.net/u/195065/blog/193614 目录[-] 一致性 hash 算法( consistent hashing ) 1 基本场景 2 hash 算法和单调性 3 consistent hashing 算法的原理 3.1 环形hash 空间 3.2 把对象映射到hash 空间 3.3 把cache 映射到hash 空间 3.4 把对象映射到cache 3.5

hash算法打散存储文件

1.首先,为防止一个目录下面出现太多文件,所以使用hash算法打散存储 举例代码: int hashcode = filename.hashCode();//得到hashCode int dir1 = hashcode & 0xf; //得到名为1到16的下及文件夹 int dir2 = (hashcode & 0xf0) >> 4; //得到名为1到16的下下及文件夹 String dir = savePath + "\\" + dir1 + "

Nginx+Memcache+一致性hash算法 实现页面分布式缓存(转)

网站响应速度优化包括集群架构中很多方面的瓶颈因素,这里所说的将页面静态化.实现分布式高速缓存就是其中的一个很好的解决方案... 1)先来看看Nginx负载均衡 Nginx负载均衡依赖自带的 ngx_http_upstream_module . ngx_http_memcached_module两大功能模块,其中一致性hash算法Nginx本身是不支持的,可以借助第三方模块: ngx_http_upstream_consistent_hash 或者直接使用淘宝的Tengine: http://te

一致性hash算法 - consistent hashing

1.背景 我们都知道memcached服务器是不提供分布式功能的,memcached的分布式完全是由客户端来实现的.在部署memcached服务器集群时,我们需要把缓存请求尽可能分散到不同的缓存服务器中,这样可以使得所有的缓存空间都得到利用,而且可以降低单独一台缓存服务器的压力.     最简单的一种实现是,缓存请求时通过计算key的哈希值,取模后映射到不同的memcahed服务器.这种简单的实现在不考虑集群机器动态变化的情况下也是比较有效的一种方案,但是,在分布式集群系统中,简单取模的哈希算法

java8 hash算法

一.hash算法 哈希算法将任意长度的二进制值映射为较短的固定长度的二进制值,这个小的二进制值称为哈希值.哈希值是一段数据唯一且极其紧凑的数值表示形式.如果散列一段明文而且哪怕只更改该段落的一个字母,随后的哈希都将产生不同的值.要找到散列为同一个值的两个不同的输入,在计算上是不可能的,所以数据的哈希值可以检验数据的完整性.一般用于快速查找和加密算法. 二.jdk的hash算法实现 (1)Interger private final int value; @Override public int

一致性hash算法

一致性哈希算法在1997年由麻省理工学院提出的一种分布式哈希(DHT)实现算法,设计目标是为了解决因特网中的热点(Hot spot)问题,初衷和CARP十分类似.一致性哈希修正了CARP使用的简单哈希算法带来的问题,使得分布式哈希(DHT)可以在P2P环境中真正得到应用. 一致性hash算法提出了在动态变化的Cache环境中,判定哈希算法好坏的四个定义: 1.平衡性(Balance):平衡性是指哈希的结果能够尽可能分布到所有的缓冲中去,这样可以使得所有的缓冲空间都得到利用.很多哈希算法都能够满足

分布式memcached学习(四)&mdash;&mdash; 一致性hash算法原理

    分布式一致性hash算法简介 当你看到"分布式一致性hash算法"这个词时,第一时间可能会问,什么是分布式,什么是一致性,hash又是什么.在分析分布式一致性hash算法原理之前,我们先来了解一下这几个概念. 分布式 分布式(distributed)是指在多台不同的服务器中部署不同的服务模块,通过远程调用协同工作,对外提供服务. 以一个航班订票系统为例,这个航班订票系统有航班预定.网上值机.旅客信息管理.订单管理.运价计算等服务模块.现在要以集中式(集群,cluster)和分布

分布式算法(一致性Hash算法)

一.分布式算法 在做服务器负载均衡时候可供选择的负载均衡的算法有很多,包括: 轮循算法(Round Robin).哈希算法(HASH).最少连接算法(Least Connection).响应速度算法(Response Time).加权法(Weighted )等.其中哈希算法是最为常用的算法. 典型的应用场景是: 有N台服务器提供缓存服务,需要对服务器进行负载均衡,将请求平均分发到每台服务器上,每台机器负责1/N的服务. 常用的算法是对hash结果取余数 (hash() mod N ):对机器编号

对一致性Hash算法,Java代码实现的深入研究

一致性Hash算法 关于一致性Hash算法,在我之前的博文中已经有多次提到了,MemCache超详细解读一文中"一致性Hash算法"部分,对于为什么要使用一致性Hash算法和一致性Hash算法的算法原理做了详细的解读. 算法的具体原理这里再次贴上: 先构造一个长度为232的整数环(这个环被称为一致性Hash环),根据节点名称的Hash值(其分布为[0, 232-1])将服务器节点放置在这个Hash环上,然后根据数据的Key值计算得到其Hash值(其分布也为[0, 232-1]),接着在