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