目錄打散-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個文件夾下。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM