前幾篇說了文件上傳,都是上傳到了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個文件夾下。