get fucked
parent
3ef04a43ea
commit
0ab477314e
|
@ -8,18 +8,18 @@ import java.io.DataInputStream;
|
||||||
import java.io.DataOutputStream;
|
import java.io.DataOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
public class FileChunkingWriter {
|
public class ChunkedCompressedChecksumFileWriter {
|
||||||
|
|
||||||
private final DataOutputStream writer;
|
private final DataOutputStream networkStreamWriter;
|
||||||
private final StreamingXXHash64 streamHash;
|
private final StreamingXXHash64 streamHash;
|
||||||
private final DataInputStream fileReader;
|
private final DataInputStream fileInputReader;
|
||||||
private final int bufferSize;
|
private final int bufferSize;
|
||||||
private final long seed;
|
private final long seed;
|
||||||
|
|
||||||
public FileChunkingWriter(DataOutputStream writer, DataInputStream fileReader, int bufferSize, long seed){
|
public ChunkedCompressedChecksumFileWriter(DataOutputStream networkStreamWriter, DataInputStream fileInputReader, int bufferSize, long seed){
|
||||||
this.writer = writer;
|
this.networkStreamWriter = networkStreamWriter;
|
||||||
this.streamHash = FileUtil.XX_HASH_FACTORY.newStreamingHash64(seed);
|
this.streamHash = FileUtil.XX_HASH_FACTORY.newStreamingHash64(seed);
|
||||||
this.fileReader = fileReader;
|
this.fileInputReader = fileInputReader;
|
||||||
this.bufferSize = bufferSize;
|
this.bufferSize = bufferSize;
|
||||||
this.seed = seed;
|
this.seed = seed;
|
||||||
}
|
}
|
||||||
|
@ -37,23 +37,27 @@ public class FileChunkingWriter {
|
||||||
ArrayData compressed = compress(uncompressed);
|
ArrayData compressed = compress(uncompressed);
|
||||||
|
|
||||||
// write data
|
// write data
|
||||||
writer.writeInt(uncompressed.length);
|
writeHeader(uncompressed.length, compressed.getActualLength(), hash);
|
||||||
writer.writeInt(compressed.getActualLength());
|
networkStreamWriter.write(compressed.getData(), 0, compressed.getActualLength());
|
||||||
writer.writeLong(hash);
|
networkStreamWriter.flush();
|
||||||
writer.write(compressed.getData(), 0, compressed.getActualLength());
|
|
||||||
writer.flush();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void close() throws IOException {
|
public void close() throws IOException {
|
||||||
writer.writeInt(0);
|
networkStreamWriter.writeInt(0);
|
||||||
writer.writeLong(streamHash.getValue());
|
networkStreamWriter.writeLong(streamHash.getValue());
|
||||||
writer.flush();
|
networkStreamWriter.flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void writeHeader(int uncompressed, int compressed, long hash) throws IOException {
|
||||||
|
networkStreamWriter.writeInt(uncompressed);
|
||||||
|
networkStreamWriter.writeInt(compressed);
|
||||||
|
networkStreamWriter.writeLong(hash);
|
||||||
}
|
}
|
||||||
|
|
||||||
private byte[] readSome() throws IOException {
|
private byte[] readSome() throws IOException {
|
||||||
byte[] readBytes = new byte[Integer.min(fileReader.available(), bufferSize)];
|
byte[] readBytes = new byte[Integer.min(fileInputReader.available(), bufferSize)];
|
||||||
|
|
||||||
int totalRead = fileReader.read(readBytes);
|
int totalRead = fileInputReader.read(readBytes);
|
||||||
assert(readBytes.length == totalRead);
|
assert(readBytes.length == totalRead);
|
||||||
|
|
||||||
return readBytes;
|
return readBytes;
|
|
@ -1,12 +1,8 @@
|
||||||
package client;
|
package client;
|
||||||
|
|
||||||
import net.jpountz.lz4.LZ4BlockInputStream;
|
|
||||||
import net.jpountz.lz4.LZ4BlockOutputStream;
|
|
||||||
import net.jpountz.lz4.LZ4FrameInputStream;
|
|
||||||
import net.jpountz.lz4.LZ4FrameOutputStream;
|
|
||||||
import server.Server;
|
import server.Server;
|
||||||
import shared.ExceptionLogger;
|
import shared.ExceptionLogger;
|
||||||
import shared.FileHeader;
|
import shared.FileUtil;
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
import java.net.Socket;
|
import java.net.Socket;
|
||||||
|
@ -14,12 +10,6 @@ import java.util.*;
|
||||||
|
|
||||||
public class Client {
|
public class Client {
|
||||||
|
|
||||||
public static class ClientInvalidUsageException extends RuntimeException {
|
|
||||||
public ClientInvalidUsageException(String str){
|
|
||||||
super(str);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private final Socket serverConnection;
|
private final Socket serverConnection;
|
||||||
private final DataOutputStream out;
|
private final DataOutputStream out;
|
||||||
private final DataInputStream in;
|
private final DataInputStream in;
|
||||||
|
@ -30,19 +20,13 @@ public class Client {
|
||||||
in = new DataInputStream(new BufferedInputStream(serverConnection.getInputStream()));
|
in = new DataInputStream(new BufferedInputStream(serverConnection.getInputStream()));
|
||||||
}
|
}
|
||||||
|
|
||||||
void sendFile(String path){
|
public Client sendFile(String path){
|
||||||
if (new File(path).isDirectory())
|
|
||||||
throw new ClientInvalidUsageException("Unable to send directory. Did you mean sendDir()?");
|
|
||||||
System.out.println("Sending path " + path);
|
System.out.println("Sending path " + path);
|
||||||
new FileHeader(path).write(out);
|
FileUtil.write(path, out);
|
||||||
try {
|
return this;
|
||||||
out.flush();
|
|
||||||
} catch (IOException e) {
|
|
||||||
ExceptionLogger.log(e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void sendDir(String path){
|
public Client sendDir(String path){
|
||||||
File p = new File(path);
|
File p = new File(path);
|
||||||
ArrayDeque<File> filesToCheck = new ArrayDeque<>(Arrays.asList(Objects.requireNonNull(p.listFiles())));
|
ArrayDeque<File> filesToCheck = new ArrayDeque<>(Arrays.asList(Objects.requireNonNull(p.listFiles())));
|
||||||
while (!filesToCheck.isEmpty()) {
|
while (!filesToCheck.isEmpty()) {
|
||||||
|
@ -52,9 +36,10 @@ public class Client {
|
||||||
} else
|
} else
|
||||||
sendFile(f.getPath());
|
sendFile(f.getPath());
|
||||||
}
|
}
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
void close(){
|
public void close(){
|
||||||
try {
|
try {
|
||||||
in.close();
|
in.close();
|
||||||
out.close();
|
out.close();
|
||||||
|
@ -66,7 +51,8 @@ public class Client {
|
||||||
|
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
try {
|
try {
|
||||||
new Client("localhost", Server.SERVER_PORT).sendDir("in/");
|
new Client("localhost", Server.SERVER_PORT).sendDir("in/").close();
|
||||||
|
//new Client("localhost", Server.SERVER_PORT).sendFile("in/ihaveafile.txt").close();
|
||||||
} catch (Exception e){
|
} catch (Exception e){
|
||||||
ExceptionLogger.log(e);
|
ExceptionLogger.log(e);
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,69 @@
|
||||||
|
package server;
|
||||||
|
|
||||||
|
import net.jpountz.xxhash.StreamingXXHash64;
|
||||||
|
import shared.ArrayData;
|
||||||
|
import shared.FileUtil;
|
||||||
|
|
||||||
|
import java.io.*;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
|
||||||
|
public class ChunkedCompressedChecksumFileReader {
|
||||||
|
|
||||||
|
private final DataInputStream networkStreamReader;
|
||||||
|
private final StreamingXXHash64 streamHash;
|
||||||
|
private final DataOutputStream fileOutputWriter;
|
||||||
|
private final long seed;
|
||||||
|
|
||||||
|
public ChunkedCompressedChecksumFileReader(DataInputStream networkStreamReader, String fileOutputPath, long seed) throws IOException {
|
||||||
|
this.networkStreamReader = networkStreamReader;
|
||||||
|
this.streamHash = FileUtil.XX_HASH_FACTORY.newStreamingHash64(seed);
|
||||||
|
this.fileOutputWriter = new DataOutputStream(new BufferedOutputStream(Files.newOutputStream(Paths.get(fileOutputPath))));
|
||||||
|
this.seed = seed;
|
||||||
|
}
|
||||||
|
|
||||||
|
public FileHeader readChunk() throws IOException {
|
||||||
|
FileHeader header = readHeader();
|
||||||
|
if (header.getUncompressed() == 0)
|
||||||
|
return header;
|
||||||
|
byte[] data = readSome(header);
|
||||||
|
byte[] decompressed = decompress(header, data);
|
||||||
|
hash(header, decompressed);
|
||||||
|
fileOutputWriter.write(decompressed, 0, decompressed.length);
|
||||||
|
return header;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void close() throws IOException {
|
||||||
|
long streamHash = networkStreamReader.readLong();
|
||||||
|
if (streamHash != this.streamHash.getValue())
|
||||||
|
throw new RuntimeException("Stream total hash doesn't match the client's sent hash!");
|
||||||
|
fileOutputWriter.flush();
|
||||||
|
fileOutputWriter.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
private FileHeader readHeader() throws IOException {
|
||||||
|
return new FileHeader().read(networkStreamReader);
|
||||||
|
}
|
||||||
|
|
||||||
|
private byte[] readSome(FileHeader header) throws IOException {
|
||||||
|
byte[] data = new byte[header.getCompressed()];
|
||||||
|
int amount = networkStreamReader.read(data, 0, header.getCompressed());
|
||||||
|
assert (header.getCompressed() == amount);
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
private byte[] decompress(FileHeader header, byte[] data) {
|
||||||
|
byte[] restored = new byte[header.getUncompressed()];
|
||||||
|
int len = FileUtil.DECOMPRESSOR.decompress(data, 0, restored, 0, header.getUncompressed());
|
||||||
|
assert (header.getUncompressed() == len);
|
||||||
|
return restored;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void hash(FileHeader header, byte[] data) {
|
||||||
|
long computedHash = FileUtil.HASH_64.hash(data, 0, data.length, seed);
|
||||||
|
streamHash.update(data, 0, data.length);
|
||||||
|
if (computedHash != header.getHash())
|
||||||
|
throw new RuntimeException("Computed hash doesn't match sent hash! File corrupted?");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -1,11 +1,7 @@
|
||||||
package server;
|
package server;
|
||||||
|
|
||||||
import net.jpountz.lz4.LZ4BlockInputStream;
|
|
||||||
import net.jpountz.lz4.LZ4BlockOutputStream;
|
|
||||||
import net.jpountz.lz4.LZ4FrameInputStream;
|
|
||||||
import net.jpountz.lz4.LZ4FrameOutputStream;
|
|
||||||
import shared.ExceptionLogger;
|
import shared.ExceptionLogger;
|
||||||
import shared.FileHeader;
|
import shared.FileUtil;
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
import java.net.Socket;
|
import java.net.Socket;
|
||||||
|
@ -37,8 +33,8 @@ public class Connection implements Runnable {
|
||||||
if (in.available() > 0) {
|
if (in.available() > 0) {
|
||||||
byte command = in.readByte();
|
byte command = in.readByte();
|
||||||
|
|
||||||
if (command == FileHeader.COMMAND.WRITE.type)
|
if (command == FileUtil.COMMAND.WRITE.type)
|
||||||
FileHeader.receive(in);
|
FileUtil.receive(in);
|
||||||
}
|
}
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
|
|
|
@ -0,0 +1,36 @@
|
||||||
|
package server;
|
||||||
|
|
||||||
|
import shared.ExceptionLogger;
|
||||||
|
|
||||||
|
import java.io.DataInputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
public class FileHeader {
|
||||||
|
|
||||||
|
private int uncompressed;
|
||||||
|
private int compressed;
|
||||||
|
private long hash;
|
||||||
|
|
||||||
|
public FileHeader() {}
|
||||||
|
|
||||||
|
public FileHeader read(DataInputStream reader) throws IOException{
|
||||||
|
uncompressed = reader.readInt();
|
||||||
|
if (uncompressed == 0)
|
||||||
|
return this;
|
||||||
|
compressed = reader.readInt();
|
||||||
|
hash = reader.readLong();
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getUncompressed() {
|
||||||
|
return uncompressed;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getCompressed() {
|
||||||
|
return compressed;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getHash() {
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,111 +0,0 @@
|
||||||
package shared;
|
|
||||||
|
|
||||||
import client.Client;
|
|
||||||
import client.FileChunkingWriter;
|
|
||||||
import net.jpountz.xxhash.StreamingXXHash64;
|
|
||||||
|
|
||||||
import java.io.*;
|
|
||||||
import java.nio.file.Files;
|
|
||||||
import java.nio.file.Paths;
|
|
||||||
|
|
||||||
public class FileHeader {
|
|
||||||
|
|
||||||
public enum COMMAND {
|
|
||||||
WRITE((byte) 1);
|
|
||||||
public final byte type;
|
|
||||||
|
|
||||||
COMMAND(byte type) {
|
|
||||||
this.type = type;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private final String relative_path;
|
|
||||||
private final String full_path;
|
|
||||||
|
|
||||||
public FileHeader(String path) {
|
|
||||||
File pf = new File(path);
|
|
||||||
if (!pf.exists())
|
|
||||||
throw new Client.ClientInvalidUsageException("Unable to send a file which doesn't exist!");
|
|
||||||
if (pf.isDirectory())
|
|
||||||
throw new Client.ClientInvalidUsageException("Path is a directory unable to send!");
|
|
||||||
String workingDirectory = System.getProperty("user.dir");
|
|
||||||
this.full_path = path;
|
|
||||||
this.relative_path = path.replace(workingDirectory, "");
|
|
||||||
System.out.println(relative_path);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void write(DataOutputStream dataOut) {
|
|
||||||
try {
|
|
||||||
DataInputStream fileReader = new DataInputStream(new BufferedInputStream(Files.newInputStream(Paths.get(full_path))));
|
|
||||||
|
|
||||||
dataOut.writeByte(COMMAND.WRITE.type);
|
|
||||||
dataOut.writeUTF(relative_path);
|
|
||||||
|
|
||||||
FileChunkingWriter writer = new FileChunkingWriter(dataOut, fileReader, FileUtil.READER_SIZE, FileUtil.SEED);
|
|
||||||
|
|
||||||
while (fileReader.available() > 0)
|
|
||||||
writer.processChunk();
|
|
||||||
|
|
||||||
writer.close();
|
|
||||||
fileReader.close();
|
|
||||||
} catch (Exception e) {
|
|
||||||
ExceptionLogger.log(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void receive(DataInputStream reader) {
|
|
||||||
try {
|
|
||||||
String path = createPath(reader.readUTF());
|
|
||||||
System.out.println("Writing to file: " + path);
|
|
||||||
|
|
||||||
DataOutputStream writer = new DataOutputStream(new BufferedOutputStream(Files.newOutputStream(Paths.get(path))));
|
|
||||||
|
|
||||||
StreamingXXHash64 computedStreamHash = FileUtil.XX_HASH_FACTORY.newStreamingHash64(FileUtil.SEED);
|
|
||||||
while (true) {
|
|
||||||
int uncompressed_size = reader.readInt();
|
|
||||||
|
|
||||||
if (uncompressed_size <= 0)
|
|
||||||
break;
|
|
||||||
|
|
||||||
int compressed_size = reader.readInt();
|
|
||||||
long hash = reader.readLong();
|
|
||||||
byte[] data = new byte[compressed_size];
|
|
||||||
int amount = reader.read(data, 0, compressed_size);
|
|
||||||
|
|
||||||
assert(amount == compressed_size);
|
|
||||||
|
|
||||||
byte[] restored = new byte[uncompressed_size];
|
|
||||||
int len = FileUtil.DECOMPRESSOR.decompress(data, 0, restored, 0, uncompressed_size);
|
|
||||||
|
|
||||||
assert(len == uncompressed_size);
|
|
||||||
|
|
||||||
long computedHash = FileUtil.HASH_64.hash(restored, 0, uncompressed_size, FileUtil.SEED);
|
|
||||||
computedStreamHash.update(restored, 0, uncompressed_size);
|
|
||||||
|
|
||||||
if (hash != computedHash)
|
|
||||||
throw new RuntimeException(hash + " HELP! " + computedHash);
|
|
||||||
|
|
||||||
writer.write(restored, 0, uncompressed_size);
|
|
||||||
}
|
|
||||||
long streamHash = reader.readLong();
|
|
||||||
if (computedStreamHash.getValue() != streamHash)
|
|
||||||
throw new RuntimeException("HELP 22");
|
|
||||||
writer.flush();
|
|
||||||
writer.close();
|
|
||||||
} catch (Exception e) {
|
|
||||||
ExceptionLogger.log(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String createPath(String userFile) {
|
|
||||||
String[] pathParts = userFile.split("/");
|
|
||||||
String userDirectory = userFile.replace(pathParts[pathParts.length - 1], "");
|
|
||||||
|
|
||||||
File ld = new File(System.getProperty("user.dir") + "/write/" + userDirectory);
|
|
||||||
if (!ld.exists() && !ld.mkdirs())
|
|
||||||
System.out.println("Failed to create directory");
|
|
||||||
|
|
||||||
return System.getProperty("user.dir") + "/write/" + userFile;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,13 +1,20 @@
|
||||||
package shared;
|
package shared;
|
||||||
|
|
||||||
|
import client.ChunkedCompressedChecksumFileWriter;
|
||||||
import net.jpountz.lz4.LZ4Compressor;
|
import net.jpountz.lz4.LZ4Compressor;
|
||||||
import net.jpountz.lz4.LZ4Factory;
|
import net.jpountz.lz4.LZ4Factory;
|
||||||
import net.jpountz.lz4.LZ4FastDecompressor;
|
import net.jpountz.lz4.LZ4FastDecompressor;
|
||||||
import net.jpountz.xxhash.XXHash64;
|
import net.jpountz.xxhash.XXHash64;
|
||||||
import net.jpountz.xxhash.XXHashFactory;
|
import net.jpountz.xxhash.XXHashFactory;
|
||||||
|
import server.ChunkedCompressedChecksumFileReader;
|
||||||
|
|
||||||
|
import java.io.*;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
|
||||||
public class FileUtil {
|
public class FileUtil {
|
||||||
|
|
||||||
|
// do not change it breaks stuff
|
||||||
protected static final int READER_SIZE = 8192;
|
protected static final int READER_SIZE = 8192;
|
||||||
public static final long SEED = 691;
|
public static final long SEED = 691;
|
||||||
|
|
||||||
|
@ -18,5 +25,79 @@ public class FileUtil {
|
||||||
public static final XXHashFactory XX_HASH_FACTORY = XXHashFactory.fastestInstance();
|
public static final XXHashFactory XX_HASH_FACTORY = XXHashFactory.fastestInstance();
|
||||||
public static final XXHash64 HASH_64 = XX_HASH_FACTORY.hash64();
|
public static final XXHash64 HASH_64 = XX_HASH_FACTORY.hash64();
|
||||||
|
|
||||||
|
public enum COMMAND {
|
||||||
|
WRITE((byte) 1);
|
||||||
|
public final byte type;
|
||||||
|
|
||||||
|
COMMAND(byte type) {
|
||||||
|
this.type = type;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void write(String path, DataOutputStream dataOut) {
|
||||||
|
validatePath(path);
|
||||||
|
String relative_path = path.replace(System.getProperty("user.dir"), "");
|
||||||
|
try {
|
||||||
|
DataInputStream fileReader = new DataInputStream(new BufferedInputStream(Files.newInputStream(Paths.get(path))));
|
||||||
|
|
||||||
|
dataOut.writeByte(COMMAND.WRITE.type);
|
||||||
|
dataOut.writeUTF(relative_path);
|
||||||
|
|
||||||
|
ChunkedCompressedChecksumFileWriter writer = new ChunkedCompressedChecksumFileWriter(dataOut, fileReader, FileUtil.READER_SIZE, FileUtil.SEED);
|
||||||
|
|
||||||
|
while (fileReader.available() > 0)
|
||||||
|
writer.processChunk();
|
||||||
|
|
||||||
|
writer.close();
|
||||||
|
fileReader.close();
|
||||||
|
} catch (Exception e) {
|
||||||
|
ExceptionLogger.log(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void receive(DataInputStream dataIn) {
|
||||||
|
try {
|
||||||
|
String path = createPath(dataIn.readUTF());
|
||||||
|
System.out.println("Writing to file: " + path);
|
||||||
|
|
||||||
|
ChunkedCompressedChecksumFileReader reader = new ChunkedCompressedChecksumFileReader(dataIn, path, FileUtil.SEED);
|
||||||
|
|
||||||
|
// ugh I want while(reader.readChunk().getUncompressed()); but it makes warnings!!!
|
||||||
|
while(true) {
|
||||||
|
if (reader.readChunk().getUncompressed() == 0)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
reader.close();
|
||||||
|
System.out.println("Writing " + path + " complete");
|
||||||
|
} catch (Exception e) {
|
||||||
|
ExceptionLogger.log(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class InvalidUsageException extends RuntimeException {
|
||||||
|
public InvalidUsageException(String str) {
|
||||||
|
super(str);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void validatePath(String path) {
|
||||||
|
File pf = new File(path);
|
||||||
|
if (!pf.exists())
|
||||||
|
throw new InvalidUsageException("Unable to send a file which doesn't exist!");
|
||||||
|
if (pf.isDirectory())
|
||||||
|
throw new InvalidUsageException("Path is a directory unable to send! Did you mean sendDir()?");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String createPath(String userFile) {
|
||||||
|
String[] pathParts = userFile.split("/");
|
||||||
|
String userDirectory = userFile.replace(pathParts[pathParts.length - 1], "");
|
||||||
|
|
||||||
|
File ld = new File(System.getProperty("user.dir") + "/write/" + userDirectory);
|
||||||
|
if (!ld.exists() && !ld.mkdirs())
|
||||||
|
throw new RuntimeException("Failed to create directory");
|
||||||
|
|
||||||
|
return System.getProperty("user.dir") + "/write/" + userFile;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue