Some time ago, in the development process based on springboot, we encountered a problem: the program needs to read all the files of a directory under resource, and it needs to be able to read them in the form of file. However, after packaging, the springboot project becomes a jar package. When reading the file, an error will be reported:... cannot be resolved to absolute file path because it does not reside in the file system:jar:file:.
In general, we can read a file under resource through IO stream as follows:
Resource resource =new ClassPathResource(fileName);
InputStream is = resource.getInputStream();
At that time, sometimes you need to use File: resource.getFile(), at this time, the above error will be reported under the jar package. But what if it's a folder?
After a query on the Internet, a solution is determined: save the files in the jar package to a temporary folder, and then read them in the form of File through the temporary folder. After reading the files, delete the temporary File directory.
The file copy code is as follows:
import lombok.extern.log4j.Log4j2;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import java.io.*;
import java.net.URL;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
@Log4j2
public class FileUtils {
/**
* Copy files to destination directory
* @param resourcePath resource Folder path for
* @param tmpDir Temporary directory
* @param fileType file type
*/
public static void copyJavaResourceDir2TmpDir(String resourcePath, String tmpDir, FileType fileType) {
Map<String, Object> fileMap = new HashMap<>();
if (resourcePath.endsWith("/")) {
resourcePath = resourcePath.substring(0, resourcePath.lastIndexOf("/"));
}
try {
Enumeration resources = null;
try {
resources = Thread.currentThread().getContextClassLoader().getResources(resourcePath);
} catch (Exception ex) {
ex.printStackTrace();
}
if (resources == null || !resources.hasMoreElements()) {
resources = FileUtils.class.getClassLoader().getResources(resourcePath);
}
while (resources.hasMoreElements()) {
URL resource = (URL) resources.nextElement();
if (resource.getProtocol().equals("file")) { // resource is a file
continue;
}
String[] split = resource.toString().split(":");
String filepath = split[2];
if (OperatingSystem.isWindows()) {
filepath = filepath + ":" + split[3];
}
String[] split2 = filepath.split("!");
String zipFileName = split2[0];
ZipFile zipFile = new ZipFile(zipFileName);
Enumeration entries = zipFile.entries();
while (entries.hasMoreElements()) {
ZipEntry entry = (ZipEntry) entries.nextElement();
String entryName = entry.getName();
if (entry.isDirectory()) {
continue;
}
if (entryName.contains(resourcePath) && entryName.endsWith(fileType.toString().toLowerCase())) {
String dir = entryName.substring(0, entryName.lastIndexOf("/"));
if (!dir.endsWith(resourcePath)) { // Destination path contains subdirectories
dir = dir.substring(dir.indexOf(resourcePath) + resourcePath.length() + 1);
if (dir.contains("/")) { // Multi level subdirectory
String[] subDir = dir.split("/");
Map<String, Object> map = fileMap;
for (String d : subDir) {
map = makeMapDir(map, d);
}
map.putAll(readOneFromJar(zipFile.getInputStream(entry), entryName));
} else { //First level subdirectory
if (fileMap.get(dir) == null) {
fileMap.put(dir, new HashMap<String, Object>());
}
((Map<String, Object>) fileMap.get(dir))
.putAll(readOneFromJar(zipFile.getInputStream(entry), entryName));
}
} else { // Destination path does not contain subdirectories
fileMap.putAll(readOneFromJar(zipFile.getInputStream(entry), entryName));
}
}
}
}
} catch (Exception e) {
log.error("read resource File exception:", e);
} finally {
try {
// Write to target cache path
createFile(fileMap, tmpDir);
} catch (Exception e) {
log.error("Exception creating temporary file:", e);
}
}
}
private static void createFile(Map<String, Object> fileMap, String targetDir) {
fileMap.forEach((key, value) -> {
if (value instanceof Map) {
createFile((Map<String, Object>) value, targetDir + File.separator + key);
} else {
createNewFile(targetDir + File.separator + key, value.toString());
}
});
}
public static void createNewFile(String filePath, String value) {
try {
File file = new File(filePath);
if (!file.exists()) {
File parentDir = file.getParentFile();
if (!parentDir.exists()) {
parentDir.mkdirs();
}
file.createNewFile();
}
PrintWriter out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file), "utf-8")));
out.write(value);
out.flush();
out.close();
} catch (Exception e) {
log.error("Exception creating file:", e);
}
}
private static Map<String, Object> makeMapDir(Map<String, Object> fileMap, String d) {
if (fileMap.get(d) == null) {
fileMap.put(d, new HashMap<String, Object>());
}
return (Map<String, Object>) fileMap.get(d);
}
public static Map<String, String> readOneFromJar(InputStream inputStream, String entryName) {
Map<String, String> fileMap = new HashMap<>();
try (Reader reader = new InputStreamReader(inputStream, "utf-8")) {
StringBuilder builder = new StringBuilder();
int ch = 0;
while ((ch = reader.read()) != -1) {
builder.append((char) ch);
}
String filename = entryName.substring(entryName.lastIndexOf("/") + 1);
fileMap.put(filename, builder.toString());
} catch (Exception ex) {
log.error("Exception reading file:", ex);
}
return fileMap;
}
public enum FileType {
XSD, TXT, XLS, XLSX, DOC, DOCX, XML, SQL, PROPERTIES, SH
}
}
The program call code is as follows:
String tmpDir = System.getProperty("user.dir") + File.separator +"tmp";
try {
FileUtils.copyJavaResourceDir2TmpDir("xsd", tmpDir, FileUtils.FileType.XSD);
loadXsd(new File(tmpDir));
} catch (Exception e) {
logger.error("Load xsd File exception:", e);
} finally {
FileUtils.delete(tmpDir);
}
In this way, you don't need to deploy the required files and jar s together every time you deploy an instance, which is much more convenient.
OperatingSystem It's the code copied from Filnk