Skip to content

Commit 8a50dc4

Browse files
committed
Updated ipfs.add to support adding directories recursively
1 parent 65d0cd8 commit 8a50dc4

File tree

4 files changed

+111
-6
lines changed

4 files changed

+111
-6
lines changed

src/main/java/org/ipfs/api/IPFS.java

100644100755
+18-3
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import java.io.*;
44
import java.net.*;
55
import java.util.*;
6+
import java.util.function.*;
67
import java.util.stream.*;
78

89
public class IPFS {
@@ -61,13 +62,23 @@ public IPFS(String host, int port, String version) {
6162
}
6263

6364
public MerkleNode add(NamedStreamable file) throws IOException {
64-
return add(Arrays.<NamedStreamable>asList(file)).get(0);
65+
List<MerkleNode> addParts = add(Collections.singletonList(file));
66+
Optional<MerkleNode> sameName = addParts.stream()
67+
.filter(node -> node.name.equals(file.getName()))
68+
.findAny();
69+
if (sameName.isPresent())
70+
return sameName.get();
71+
return addParts.get(0);
6572
}
6673

6774
public List<MerkleNode> add(List<NamedStreamable> files) throws IOException {
6875
Multipart m = new Multipart("http://" + host + ":" + port + version+"add?stream-channels=true", "UTF-8");
69-
for (NamedStreamable f : files)
70-
m.addFilePart("file", f);
76+
for (NamedStreamable file: files) {
77+
if (file.isDirectory()) {
78+
m.addSubtree("", ((NamedStreamable.FileWrapper)file).getFile());
79+
} else
80+
m.addFilePart("file", file);
81+
};
7182
String res = m.finish();
7283
return JSONParser.parseStream(res).stream()
7384
.map(x -> MerkleNode.fromJSON((Map<String, Object>) x))
@@ -83,6 +94,10 @@ public byte[] cat(Multihash hash) throws IOException {
8394
return retrieve("cat/" + hash);
8495
}
8596

97+
public byte[] cat(Multihash hash, String subPath) throws IOException {
98+
return retrieve("cat?arg=" + hash + URLEncoder.encode(subPath, "UTF-8"));
99+
}
100+
86101
public byte[] get(Multihash hash) throws IOException {
87102
return retrieve("get/" + hash);
88103
}

src/main/java/org/ipfs/api/Multipart.java

100644100755
+26-1
Original file line numberDiff line numberDiff line change
@@ -48,13 +48,38 @@ public void addFormField(String name, String value) {
4848
writer.flush();
4949
}
5050

51+
public void addSubtree(String path, File dir) throws IOException {
52+
String dirPath = path + (path.length() > 0 ? "/" : "") + dir.getName();
53+
addDirectoryPart(dirPath);
54+
for (File f: dir.listFiles()) {
55+
if (f.isDirectory())
56+
addSubtree(dirPath, f);
57+
else
58+
addFilePart("file", new NamedStreamable.FileWrapper(dirPath + "/", f));
59+
}
60+
}
61+
62+
public void addDirectoryPart(String path) {
63+
try {
64+
writer.append("--" + boundary).append(LINE_FEED);
65+
writer.append("Content-Disposition: file; filename=\"" + URLEncoder.encode(path, "UTF-8") + "\"").append(LINE_FEED);
66+
writer.append("Content-Type: application/x-directory").append(LINE_FEED);
67+
writer.append("Content-Transfer-Encoding: binary").append(LINE_FEED);
68+
writer.append(LINE_FEED);
69+
writer.append(LINE_FEED);
70+
writer.flush();
71+
} catch (UnsupportedEncodingException e) {
72+
throw new RuntimeException(e);
73+
}
74+
}
75+
5176
public void addFilePart(String fieldName, NamedStreamable uploadFile) throws IOException {
5277
Optional<String> fileName = uploadFile.getName();
5378
writer.append("--" + boundary).append(LINE_FEED);
5479
if (!fileName.isPresent())
5580
writer.append("Content-Disposition: file; name=\"" + fieldName + "\";").append(LINE_FEED);
5681
else
57-
writer.append("Content-Disposition: file; name=\"" + fieldName + "\"; filename=\"" + fileName.get() + "\"").append(LINE_FEED);
82+
writer.append("Content-Disposition: file; filename=\"" + fileName.get() + "\"").append(LINE_FEED);
5883
writer.append("Content-Type: application/octet-stream").append(LINE_FEED);
5984
writer.append("Content-Transfer-Encoding: binary").append(LINE_FEED);
6085
writer.append(LINE_FEED);

src/main/java/org/ipfs/api/NamedStreamable.java

100644100755
+27-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package org.ipfs.api;
22

33
import java.io.*;
4+
import java.net.*;
45
import java.util.*;
56

67
public interface NamedStreamable
@@ -9,6 +10,8 @@ public interface NamedStreamable
910

1011
Optional<String> getName();
1112

13+
boolean isDirectory();
14+
1215
default byte[] getContents() throws IOException {
1316
InputStream in = getInputStream();
1417
ByteArrayOutputStream bout = new ByteArrayOutputStream();
@@ -21,17 +24,35 @@ default byte[] getContents() throws IOException {
2124

2225
class FileWrapper implements NamedStreamable {
2326
private final File source;
27+
private final String pathPrefix;
2428

25-
public FileWrapper(File source) {
29+
public FileWrapper(String pathPrefix, File source) {
2630
this.source = source;
31+
this.pathPrefix = pathPrefix;
32+
}
33+
34+
public FileWrapper(File source) {
35+
this("", source);
2736
}
2837

2938
public InputStream getInputStream() throws IOException {
3039
return new FileInputStream(source);
3140
}
3241

42+
public boolean isDirectory() {
43+
return source.isDirectory();
44+
}
45+
46+
public File getFile() {
47+
return source;
48+
}
49+
3350
public Optional<String> getName() {
34-
return Optional.of(source.getName());
51+
try {
52+
return Optional.of(URLEncoder.encode(pathPrefix + source.getName(), "UTF-8"));
53+
} catch (UnsupportedEncodingException e) {
54+
throw new RuntimeException(e);
55+
}
3556
}
3657
}
3758

@@ -52,6 +73,10 @@ public ByteArrayWrapper(Optional<String> name, byte[] data) {
5273
this.data = data;
5374
}
5475

76+
public boolean isDirectory() {
77+
return false;
78+
}
79+
5580
public InputStream getInputStream() throws IOException {
5681
return new ByteArrayInputStream(data);
5782
}

src/test/java/org/ipfs/api/APITests.java

100644100755
+40
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,46 @@ public void singleFileTest() {
2525
fileTest(file);
2626
}
2727

28+
@org.junit.Test
29+
public void directoryTest() throws IOException {
30+
Random rnd = new Random();
31+
String dirName = "folder" + rnd.nextInt(100);
32+
Path tmpDir = Files.createTempDirectory(dirName);
33+
34+
String fileName = "afile" + rnd.nextInt(100);
35+
Path file = tmpDir.resolve(fileName);
36+
FileOutputStream fout = new FileOutputStream(file.toFile());
37+
byte[] fileContents = "IPFS rocks!".getBytes();
38+
fout.write(fileContents);
39+
fout.flush();
40+
fout.close();
41+
42+
String subdirName = "subdir";
43+
tmpDir.resolve(subdirName).toFile().mkdir();
44+
45+
String subfileName = "subdirfile" + rnd.nextInt(100);
46+
Path subdirfile = tmpDir.resolve(subdirName + "/" + subfileName);
47+
FileOutputStream fout2 = new FileOutputStream(subdirfile.toFile());
48+
byte[] file2Contents = "IPFS still rocks!".getBytes();
49+
fout2.write(file2Contents);
50+
fout2.flush();
51+
fout2.close();
52+
53+
MerkleNode addResult = ipfs.add(new NamedStreamable.FileWrapper(tmpDir.toFile()));
54+
List<MerkleNode> lsResult = ipfs.ls(addResult.hash);
55+
if (lsResult.size() != 1)
56+
throw new IllegalStateException("Incorrect number of objects in ls!");
57+
if (!lsResult.get(0).equals(addResult))
58+
throw new IllegalStateException("Object not returned in ls!");
59+
byte[] catResult = ipfs.cat(addResult.hash, "/" + fileName);
60+
if (!Arrays.equals(catResult, fileContents))
61+
throw new IllegalStateException("Different contents!");
62+
63+
byte[] catResult2 = ipfs.cat(addResult.hash, "/" + subdirName + "/" + subfileName);
64+
if (!Arrays.equals(catResult2, file2Contents))
65+
throw new IllegalStateException("Different contents!");
66+
}
67+
2868
// @org.junit.Test
2969
public void largeFileTest() {
3070
byte[] largerData = new byte[100*1024*1024];

0 commit comments

Comments
 (0)