001package gu.sql2java.store; 002 003import static gu.sql2java.store.BinaryUtils.saveBytes; 004 005import java.io.File; 006import java.io.FileInputStream; 007import java.io.FileNotFoundException; 008import java.io.FilenameFilter; 009import java.io.IOException; 010import java.io.InputStream; 011import java.net.MalformedURLException; 012import java.net.URL; 013import java.net.URLConnection; 014import java.net.URLStreamHandler; 015import java.nio.file.Files; 016import java.nio.file.Path; 017import java.nio.file.Paths; 018 019import com.google.common.base.ConditionChecks; 020 021/** 022 * 二进制数据本地存储实现 023 * @author guyadong 024 * 025 */ 026public class LocalBinaryStore extends BaseURLStore { 027 private static final String ORIGIN_ROOT_NAME = "origin"; 028 private static final String PROTOCOL = "lbs"; 029 private final URLStreamHandler handler = new Handler(); 030 private File storeRoot; 031 public static final LocalBinaryStore SINGLETON = new LocalBinaryStore(); 032 private LocalBinaryStore() { 033 } 034 035 /** 036 * 创建iadb存储地址对象(将path中storeRoot路径剥离) 037 * @param file 038 * @return 039 */ 040 protected URL createStoreURL(File file){ 041 try { 042 return new URL(PROTOCOL, null,file.toURI().toURL().getPath().substring(getStoreRoot().toURI().toURL().getPath().length())); 043 } catch (MalformedURLException e) { 044 throw new RuntimeException(e); 045 } 046 } 047 @Override 048 protected boolean doExists(URL storedURL){ 049 return Files.isReadable(pathOf(storedURL)); 050 } 051 @Override 052 protected URL doFind(final String md5) { 053 File folder = relativeFolderPath(md5).toFile(); 054 if(folder.isDirectory()){ 055 File[] files = folder.listFiles(new FilenameFilter() { 056 @Override 057 public boolean accept(File dir, String name) { 058 return name.startsWith(md5); 059 } 060 }); 061 try { 062 return files.length >0 ? files[0].toURI().toURL() : null; 063 } catch (MalformedURLException e) { 064 throw new RuntimeException(e); 065 } 066 } 067 return null; 068 } 069 070 protected Path pathOf(URL storedURL) { 071 // 在path中添加root路径 072 return Paths.get(getStoreRoot().getPath(),storedURL.getPath()); 073 } 074 @Override 075 protected URL doStore(byte[] binary, String md5, String extension, boolean makeURLOnly) throws IOException { 076 File dst = createDestFile(md5, extension); 077 if(!makeURLOnly){ 078 if(dst.length() != binary.length){ 079 saveBytes(binary, dst, true); 080 } 081 } 082 return createStoreURL(dst); 083 } 084 @Override 085 protected boolean doDelete(URL storedURL) throws IOException{ 086 return Files.deleteIfExists(pathOf(storedURL)); 087 } 088 089 @Override 090 public final String getProtocol() { 091 return PROTOCOL; 092 } 093 094 public LocalBinaryStore setStoreRoot(File storeRoot) { 095 this.storeRoot = storeRoot; 096 return this; 097 } 098 099 private File getStoreRoot() { 100 return ConditionChecks.checkNotNull(storeRoot, 101 IllegalStateException.class, 102 "storeRoot is uninitialized,please call setStoreRoot(File) firstly"); 103 } 104 105 protected String relativeFilePath(String md5, String suffix){ 106 // md5前两位做第一级子目录,接下来的两位做第二级子目录 107 String s = (suffix == null || suffix.length() == 0) ? "" : "." + suffix; 108 Path p = Paths.get(ORIGIN_ROOT_NAME, md5.substring(0, 2),md5.substring(2, 4),md5 + s); 109 return p.toString(); 110 } 111 protected Path relativeFolderPath(String md5){ 112 // md5前两位做第一级子目录,接下来的两位做第二级子目录 113 return Paths.get(ORIGIN_ROOT_NAME, md5.substring(0, 2),md5.substring(2, 4)); 114 } 115 private File createDestFile(String md5, String suffix){ 116 return new File(getStoreRoot(),relativeFilePath(md5, suffix)); 117 } 118 119 @Override 120 protected URLStreamHandler doGetURLStreamHandler() { 121 return handler; 122 } 123 124 @Override 125 public int hashCode() { 126 final int prime = 31; 127 int result = super.hashCode(); 128 result = prime * result + ((getProtocol() == null) ? 0 : getProtocol().hashCode()); 129 result = prime * result + ((storeRoot == null) ? 0 : storeRoot.hashCode()); 130 return result; 131 } 132 133 @Override 134 public boolean equals(Object obj) { 135 if (this == obj) 136 return true; 137 if (!super.equals(obj)) 138 return false; 139 if (!(obj instanceof LocalBinaryStore)) 140 return false; 141 LocalBinaryStore other = (LocalBinaryStore) obj; 142 if (getProtocol() == null) { 143 if (other.getProtocol() != null) 144 return false; 145 } else if (!getProtocol().equals(other.getProtocol())) 146 return false; 147 if (storeRoot == null) { 148 if (other.storeRoot != null) 149 return false; 150 } else if (!storeRoot.equals(other.storeRoot)) 151 return false; 152 return true; 153 } 154 155 @Override 156 public String toString() { 157 StringBuilder builder = new StringBuilder(); 158 builder.append("LocalBinaryStore [protocol="); 159 builder.append(getProtocol()); 160 builder.append(",storeRoot="); 161 builder.append(storeRoot); 162 builder.append("]"); 163 return builder.toString(); 164 } 165 166 private class Handler extends URLStreamHandler { 167 @Override 168 protected URLConnection openConnection(URL u) throws IOException { 169 return new FileConnection(u); 170 } 171 } 172 173 private class FileConnection extends URLConnection{ 174 175 protected FileConnection(URL url) { 176 super(url); 177 } 178 179 @Override 180 public void connect() throws IOException { 181 connected = true; 182 } 183 184 @Override 185 public InputStream getInputStream() throws IOException { 186 connect(); 187 try { 188 return new FileInputStream(pathOf(url).toFile()); 189 } catch (FileNotFoundException e) { 190 throw new DataNotFoundException(url,e); 191 } 192 } 193 194 } 195}