Skip to content

Commit

Permalink
Updated ipfs.add to support adding directories recursively
Browse files Browse the repository at this point in the history
  • Loading branch information
ianopolous committed Oct 3, 2016
1 parent 65d0cd8 commit 8a50dc4
Show file tree
Hide file tree
Showing 4 changed files with 111 additions and 6 deletions.
21 changes: 18 additions & 3 deletions src/main/java/org/ipfs/api/IPFS.java
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import java.io.*;
import java.net.*;
import java.util.*;
import java.util.function.*;
import java.util.stream.*;

public class IPFS {
Expand Down Expand Up @@ -61,13 +62,23 @@ public IPFS(String host, int port, String version) {
}

public MerkleNode add(NamedStreamable file) throws IOException {
return add(Arrays.<NamedStreamable>asList(file)).get(0);
List<MerkleNode> addParts = add(Collections.singletonList(file));
Optional<MerkleNode> sameName = addParts.stream()
.filter(node -> node.name.equals(file.getName()))
.findAny();
if (sameName.isPresent())
return sameName.get();
return addParts.get(0);
}

public List<MerkleNode> add(List<NamedStreamable> files) throws IOException {
Multipart m = new Multipart("http://" + host + ":" + port + version+"add?stream-channels=true", "UTF-8");
for (NamedStreamable f : files)
m.addFilePart("file", f);
for (NamedStreamable file: files) {
if (file.isDirectory()) {
m.addSubtree("", ((NamedStreamable.FileWrapper)file).getFile());
} else
m.addFilePart("file", file);
};
String res = m.finish();
return JSONParser.parseStream(res).stream()
.map(x -> MerkleNode.fromJSON((Map<String, Object>) x))
Expand All @@ -83,6 +94,10 @@ public byte[] cat(Multihash hash) throws IOException {
return retrieve("cat/" + hash);
}

public byte[] cat(Multihash hash, String subPath) throws IOException {
return retrieve("cat?arg=" + hash + URLEncoder.encode(subPath, "UTF-8"));
}

public byte[] get(Multihash hash) throws IOException {
return retrieve("get/" + hash);
}
Expand Down
27 changes: 26 additions & 1 deletion src/main/java/org/ipfs/api/Multipart.java
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -48,13 +48,38 @@ public void addFormField(String name, String value) {
writer.flush();
}

public void addSubtree(String path, File dir) throws IOException {
String dirPath = path + (path.length() > 0 ? "/" : "") + dir.getName();
addDirectoryPart(dirPath);
for (File f: dir.listFiles()) {
if (f.isDirectory())
addSubtree(dirPath, f);
else
addFilePart("file", new NamedStreamable.FileWrapper(dirPath + "/", f));
}
}

public void addDirectoryPart(String path) {
try {
writer.append("--" + boundary).append(LINE_FEED);
writer.append("Content-Disposition: file; filename=\"" + URLEncoder.encode(path, "UTF-8") + "\"").append(LINE_FEED);
writer.append("Content-Type: application/x-directory").append(LINE_FEED);
writer.append("Content-Transfer-Encoding: binary").append(LINE_FEED);
writer.append(LINE_FEED);
writer.append(LINE_FEED);
writer.flush();
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
}

public void addFilePart(String fieldName, NamedStreamable uploadFile) throws IOException {
Optional<String> fileName = uploadFile.getName();
writer.append("--" + boundary).append(LINE_FEED);
if (!fileName.isPresent())
writer.append("Content-Disposition: file; name=\"" + fieldName + "\";").append(LINE_FEED);
else
writer.append("Content-Disposition: file; name=\"" + fieldName + "\"; filename=\"" + fileName.get() + "\"").append(LINE_FEED);
writer.append("Content-Disposition: file; filename=\"" + fileName.get() + "\"").append(LINE_FEED);
writer.append("Content-Type: application/octet-stream").append(LINE_FEED);
writer.append("Content-Transfer-Encoding: binary").append(LINE_FEED);
writer.append(LINE_FEED);
Expand Down
29 changes: 27 additions & 2 deletions src/main/java/org/ipfs/api/NamedStreamable.java
100644 → 100755
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package org.ipfs.api;

import java.io.*;
import java.net.*;
import java.util.*;

public interface NamedStreamable
Expand All @@ -9,6 +10,8 @@ public interface NamedStreamable

Optional<String> getName();

boolean isDirectory();

default byte[] getContents() throws IOException {
InputStream in = getInputStream();
ByteArrayOutputStream bout = new ByteArrayOutputStream();
Expand All @@ -21,17 +24,35 @@ default byte[] getContents() throws IOException {

class FileWrapper implements NamedStreamable {
private final File source;
private final String pathPrefix;

public FileWrapper(File source) {
public FileWrapper(String pathPrefix, File source) {
this.source = source;
this.pathPrefix = pathPrefix;
}

public FileWrapper(File source) {
this("", source);
}

public InputStream getInputStream() throws IOException {
return new FileInputStream(source);
}

public boolean isDirectory() {
return source.isDirectory();
}

public File getFile() {
return source;
}

public Optional<String> getName() {
return Optional.of(source.getName());
try {
return Optional.of(URLEncoder.encode(pathPrefix + source.getName(), "UTF-8"));
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
}
}

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

public boolean isDirectory() {
return false;
}

public InputStream getInputStream() throws IOException {
return new ByteArrayInputStream(data);
}
Expand Down
40 changes: 40 additions & 0 deletions src/test/java/org/ipfs/api/APITests.java
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,46 @@ public void singleFileTest() {
fileTest(file);
}

@org.junit.Test
public void directoryTest() throws IOException {
Random rnd = new Random();
String dirName = "folder" + rnd.nextInt(100);
Path tmpDir = Files.createTempDirectory(dirName);

String fileName = "afile" + rnd.nextInt(100);
Path file = tmpDir.resolve(fileName);
FileOutputStream fout = new FileOutputStream(file.toFile());
byte[] fileContents = "IPFS rocks!".getBytes();
fout.write(fileContents);
fout.flush();
fout.close();

String subdirName = "subdir";
tmpDir.resolve(subdirName).toFile().mkdir();

String subfileName = "subdirfile" + rnd.nextInt(100);
Path subdirfile = tmpDir.resolve(subdirName + "/" + subfileName);
FileOutputStream fout2 = new FileOutputStream(subdirfile.toFile());
byte[] file2Contents = "IPFS still rocks!".getBytes();
fout2.write(file2Contents);
fout2.flush();
fout2.close();

MerkleNode addResult = ipfs.add(new NamedStreamable.FileWrapper(tmpDir.toFile()));
List<MerkleNode> lsResult = ipfs.ls(addResult.hash);
if (lsResult.size() != 1)
throw new IllegalStateException("Incorrect number of objects in ls!");
if (!lsResult.get(0).equals(addResult))
throw new IllegalStateException("Object not returned in ls!");
byte[] catResult = ipfs.cat(addResult.hash, "/" + fileName);
if (!Arrays.equals(catResult, fileContents))
throw new IllegalStateException("Different contents!");

byte[] catResult2 = ipfs.cat(addResult.hash, "/" + subdirName + "/" + subfileName);
if (!Arrays.equals(catResult2, file2Contents))
throw new IllegalStateException("Different contents!");
}

// @org.junit.Test
public void largeFileTest() {
byte[] largerData = new byte[100*1024*1024];
Expand Down

0 comments on commit 8a50dc4

Please sign in to comment.