ResourceAdapter.java 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279
  1. package org.ssssssss.magicapi.adapter;
  2. import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
  3. import org.springframework.util.ResourceUtils;
  4. import org.ssssssss.magicapi.utils.IoUtils;
  5. import org.ssssssss.magicapi.utils.PathUtils;
  6. import java.io.File;
  7. import java.io.FileNotFoundException;
  8. import java.io.IOException;
  9. import java.net.JarURLConnection;
  10. import java.net.URL;
  11. import java.util.Arrays;
  12. import java.util.Collections;
  13. import java.util.List;
  14. import java.util.jar.JarEntry;
  15. import java.util.jar.JarFile;
  16. import java.util.stream.Collectors;
  17. import java.util.zip.ZipEntry;
  18. public abstract class ResourceAdapter {
  19. private static PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
  20. private static final String SPRING_BOOT_CLASS_PATH = "BOOT-INF/classes/";
  21. public static Resource getResource(String location) throws IOException {
  22. if (location == null) {
  23. return null;
  24. }
  25. org.springframework.core.io.Resource resource;
  26. if (location.startsWith(ResourceUtils.CLASSPATH_URL_PREFIX)) {
  27. resource = resolver.getResource(location);
  28. if (resource.exists()) {
  29. return resolveResource(resource, true);
  30. } else {
  31. throw new FileNotFoundException(String.format("%s not found", resource.getDescription()));
  32. }
  33. } else {
  34. resource = resolver.getResource(location);
  35. if (!resource.exists()) {
  36. resource = resolver.getResource(ResourceUtils.FILE_URL_PREFIX + location);
  37. }
  38. }
  39. return resolveResource(resource, false);
  40. }
  41. private static Resource resolveResource(org.springframework.core.io.Resource resource, boolean readonly) throws IOException {
  42. URL url = resource.getURI().toURL();
  43. if (url.getProtocol().equals(ResourceUtils.URL_PROTOCOL_JAR)) {
  44. JarURLConnection connection = (JarURLConnection) url.openConnection();
  45. boolean springBootClassPath = connection.getClass().getName().equals("org.springframework.boot.loader.jar.JarURLConnection");
  46. String entryName = (springBootClassPath ? SPRING_BOOT_CLASS_PATH : "") + connection.getEntryName();
  47. JarFile jarFile = connection.getJarFile();
  48. List<JarEntry> entries = jarFile.stream().filter(it -> it.getName().startsWith(entryName)).collect(Collectors.toList());
  49. return new JarResource(jarFile, entryName, entries, springBootClassPath);
  50. } else {
  51. return new FileResource(resource.getFile(), readonly);
  52. }
  53. }
  54. private static class FileResource implements Resource {
  55. private File file;
  56. private boolean readonly;
  57. public FileResource(File file, boolean readonly) {
  58. this.file = file;
  59. this.readonly = readonly;
  60. }
  61. @Override
  62. public boolean readonly() {
  63. return this.readonly;
  64. }
  65. @Override
  66. public boolean exists() {
  67. return this.file.exists();
  68. }
  69. @Override
  70. public boolean delete() {
  71. return !readonly() && IoUtils.delete(this.file);
  72. }
  73. @Override
  74. public boolean isDirectory() {
  75. return this.file.isDirectory();
  76. }
  77. @Override
  78. public boolean mkdir() {
  79. return !readonly() && this.file.mkdirs();
  80. }
  81. @Override
  82. public byte[] read() {
  83. return IoUtils.bytes(this.file);
  84. }
  85. @Override
  86. public boolean renameTo(Resource resource) {
  87. if (!this.readonly()) {
  88. File target = ((FileResource) resource).file;
  89. if (this.file.renameTo(target)) {
  90. this.file = target;
  91. return true;
  92. }
  93. }
  94. return false;
  95. }
  96. @Override
  97. public Resource getResource(String name) {
  98. return new FileResource(new File(this.file, name), this.readonly);
  99. }
  100. @Override
  101. public String name() {
  102. return this.file.getName();
  103. }
  104. @Override
  105. public List<Resource> resources() {
  106. File[] files = this.file.listFiles();
  107. return files == null ? Collections.emptyList() : Arrays.stream(files).map(it -> new FileResource(it, this.readonly)).collect(Collectors.toList());
  108. }
  109. @Override
  110. public Resource parent() {
  111. return new FileResource(this.file.getParentFile(), this.readonly);
  112. }
  113. @Override
  114. public List<Resource> dirs() {
  115. return IoUtils.dirs(this.file).stream().map(it -> new FileResource(it, this.readonly)).collect(Collectors.toList());
  116. }
  117. @Override
  118. public boolean write(byte[] bytes) {
  119. return !readonly() && IoUtils.write(this.file, bytes);
  120. }
  121. @Override
  122. public boolean write(String content) {
  123. return !readonly() && IoUtils.write(this.file, content);
  124. }
  125. @Override
  126. public List<Resource> files(String suffix) {
  127. return IoUtils.files(this.file, suffix).stream().map(it -> new FileResource(it, this.readonly)).collect(Collectors.toList());
  128. }
  129. @Override
  130. public String getAbsolutePath() {
  131. return this.file.getAbsolutePath();
  132. }
  133. @Override
  134. public String toString() {
  135. return String.format("file resource [%s]", this.file.getAbsolutePath());
  136. }
  137. }
  138. private static class JarResource implements Resource {
  139. private JarFile jarFile;
  140. private ZipEntry entry;
  141. private List<JarEntry> entries;
  142. private String entryName;
  143. private JarResource parent = this;
  144. private boolean inSpringBoot;
  145. public JarResource(JarFile jarFile, String entryName, List<JarEntry> entries, boolean inSpringBoot) {
  146. this.jarFile = jarFile;
  147. this.entryName = entryName;
  148. this.inSpringBoot = inSpringBoot;
  149. this.entry = getEntry(this.entryName);
  150. this.entries = entries;
  151. }
  152. public JarResource(JarFile jarFile, String entryName, List<JarEntry> entries, JarResource parent, boolean inSpringBoot) {
  153. this(jarFile, entryName, entries, inSpringBoot);
  154. this.parent = parent;
  155. }
  156. @Override
  157. public boolean readonly() {
  158. return true;
  159. }
  160. @Override
  161. public byte[] read() {
  162. try {
  163. return IoUtils.bytes(this.jarFile.getInputStream(entry));
  164. } catch (IOException e) {
  165. return new byte[0];
  166. }
  167. }
  168. @Override
  169. public boolean isDirectory() {
  170. return this.entry.isDirectory();
  171. }
  172. @Override
  173. public boolean exists() {
  174. return this.entry != null;
  175. }
  176. protected ZipEntry getEntry(String name) {
  177. if (inSpringBoot && name.startsWith(SPRING_BOOT_CLASS_PATH)) {
  178. name = name.substring(SPRING_BOOT_CLASS_PATH.length());
  179. }
  180. return this.jarFile.getEntry(name);
  181. }
  182. @Override
  183. public Resource getResource(String name) {
  184. String entryName = PathUtils.replaceSlash(this.entryName + "/" + name);
  185. String prefix = PathUtils.replaceSlash(entryName + "/");
  186. return new JarResource(this.jarFile, entryName, entries.stream()
  187. .filter(it -> it.getName().startsWith(prefix))
  188. .collect(Collectors.toList()), this, this.inSpringBoot);
  189. }
  190. @Override
  191. public String name() {
  192. int index = this.entryName.lastIndexOf("/");
  193. return index > -1 ? this.entryName.substring(index) : this.entryName;
  194. }
  195. @Override
  196. public Resource parent() {
  197. return this.parent;
  198. }
  199. @Override
  200. public List<Resource> dirs() {
  201. return resources().stream().filter(Resource::isDirectory).collect(Collectors.toList());
  202. }
  203. @Override
  204. public List<Resource> files(String suffix) {
  205. return this.entries.stream().filter(it -> it.getName().endsWith(suffix))
  206. .map(entry -> new JarResource(jarFile, entry.getName(), Collections.emptyList(), this.inSpringBoot))
  207. .collect(Collectors.toList());
  208. }
  209. @Override
  210. public List<Resource> resources() {
  211. String prefix = PathUtils.replaceSlash(this.entryName + "/");
  212. return entries.stream()
  213. .filter(it -> it.getName().startsWith(prefix))
  214. .map(entry -> new JarResource(jarFile, entry.getName(), entries.stream()
  215. .filter(item -> item.getName().startsWith(PathUtils.replaceSlash(entry.getName() + "/")))
  216. .collect(Collectors.toList()), this.inSpringBoot)
  217. )
  218. .collect(Collectors.toList());
  219. }
  220. @Override
  221. public String getAbsolutePath() {
  222. return this.jarFile.getName() + "/" + this.entryName;
  223. }
  224. @Override
  225. public String toString() {
  226. return String.format("class path resource [%s]", this.entryName);
  227. }
  228. }
  229. }