zoukankan      html  css  js  c++  java
  • 目录打散-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("文件保存位置:
    "+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:zhcwapache-tomcat-7.0.69webappsUploadup/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个文件夹下。

  • 相关阅读:
    pychram 2018-01 安装pyQT5报错
    pyqt 8行内就可以跑一个浏览器
    sql server无log ldf日志文件附件mdf数据库重新生成ldf日志文件
    解决Specifying a namespace in include()withou providing an app_name
    Java 连接池的工作原理(转)
    使用from __future__ import unicode_literals时要注意的问题
    详细介绍Redis的几种数据结构以及使用注意事项(转)
    再谈Redis应用场景(转)
    Redis的LRU机制(转)
    深入理解Redis主键失效原理及实现机制(转)
  • 原文地址:https://www.cnblogs.com/lihaoyang/p/7344937.html
Copyright © 2011-2022 走看看