• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

kit-data-manager / ro-crate-java / #389

29 Apr 2025 06:27PM UTC coverage: 89.357%. First build
#389

Pull #233

github

Pfeil
fix: add dependency on build step for Javadoc generation in CI
Pull Request #233: Version 2.1.0

608 of 700 new or added lines in 28 files covered. (86.86%)

1889 of 2114 relevant lines covered (89.36%)

0.89 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

81.63
/src/main/java/edu/kit/datamanager/ro_crate/reader/ZipStreamStrategy.java
1
package edu.kit.datamanager.ro_crate.reader;
2

3
import com.fasterxml.jackson.databind.ObjectMapper;
4
import com.fasterxml.jackson.databind.node.ObjectNode;
5
import edu.kit.datamanager.ro_crate.objectmapper.MyObjectMapper;
6
import java.io.File;
7
import java.io.FileOutputStream;
8
import java.io.IOException;
9
import java.io.InputStream;
10
import java.io.OutputStream;
11
import java.nio.file.Path;
12
import java.util.UUID;
13
import net.lingala.zip4j.io.inputstream.ZipInputStream;
14
import net.lingala.zip4j.model.LocalFileHeader;
15
import org.apache.commons.io.FileUtils;
16
import org.slf4j.Logger;
17
import org.slf4j.LoggerFactory;
18

19
/**
20
 * A ZIP file reader implementation of the StreamReaderStrategy interface.
21
 * This class handles reading and extraction of RO-Crate content from ZIP archives
22
 * into a temporary directory structure, which allows for accessing the contained files.
23
 *
24
 * @author jejkal
25
 */
26
public class ZipStreamStrategy implements GenericReaderStrategy<InputStream> {
27

28
    private static final Logger logger = LoggerFactory.getLogger(ZipStreamStrategy.class);
1✔
29
    protected final String ID = UUID.randomUUID().toString();
1✔
30
    protected Path temporaryFolder = Path.of(String.format("./.tmp/ro-crate-java/zipStreamReader/%s/", ID));
1✔
31
    protected boolean isExtracted = false;
1✔
32

33
    /**
34
     * Crates a ZipStreamReader with the default configuration as described in
35
     * the class documentation.
36
     */
37
    public ZipStreamStrategy() {
1✔
38
    }
1✔
39

40
    /**
41
     * Creates a ZipStreamReader which will extract the contents temporary to
42
     * the given location instead of the default location.
43
     *
44
     * @param folderPath the custom directory to extract content to for
45
     * temporary access.
46
     * @param shallAddUuidSubfolder if true, the reader will extract into
47
     * subdirectories of the given directory. These subdirectories will have
48
     * UUIDs as their names.
49
     */
50
    public ZipStreamStrategy(Path folderPath, boolean shallAddUuidSubfolder) {
1✔
51
        if (shallAddUuidSubfolder) {
1✔
52
            this.temporaryFolder = folderPath.resolve(ID);
1✔
53
        } else {
54
            this.temporaryFolder = folderPath;
1✔
55
        }
56
    }
1✔
57

58
    /**
59
     * @return the identifier which may be used as the name for a subfolder in
60
     * the temporary directory.
61
     */
62
    public String getID() {
63
        return ID;
1✔
64
    }
65

66
    /**
67
     * @return the folder (considered temporary) where the zipped crate will be
68
     * or has been extracted to.
69
     */
70
    public Path getTemporaryFolder() {
71
        return temporaryFolder;
1✔
72
    }
73

74
    /**
75
     * @return whether the crate has already been extracted into the temporary
76
     * folder.
77
     */
78
    public boolean isExtracted() {
79
        return isExtracted;
1✔
80
    }
81

82
    /**Read the crate metadata and content from the provided input stream.
83
     * 
84
     * @param stream The input stream.
85
     */
86
    private void readCrate(InputStream stream) {
87
        try {
88
            File folder = temporaryFolder.toFile();
1✔
89
            // ensure the directory is clean
90
            if (folder.exists()) {
1✔
91
                if (folder.isDirectory()) {
1✔
92
                    FileUtils.cleanDirectory(folder);
1✔
NEW
93
                } else if (folder.isFile()) {
×
NEW
94
                    FileUtils.delete(folder);
×
95
                }
96
            } else {
97
                FileUtils.forceMkdir(folder);
1✔
98
            }
99

100
            LocalFileHeader localFileHeader;
101
            int readLen;
102
            byte[] readBuffer = new byte[4096];
1✔
103

104
            try (ZipInputStream zipInputStream = new ZipInputStream(stream)) {
1✔
105
                while ((localFileHeader = zipInputStream.getNextEntry()) != null) {
1✔
106
                    String fileName = localFileHeader.getFileName();
1✔
107
                    File extractedFile = new File(folder, fileName).getCanonicalFile();
1✔
108
                    if (!extractedFile.toPath().startsWith(folder.getCanonicalPath())) {
1✔
NEW
109
                        throw new IOException("Entry is outside of target directory: " + fileName);
×
110
                    }
111
                    try (OutputStream outputStream = new FileOutputStream(extractedFile)) {
1✔
112
                        while ((readLen = zipInputStream.read(readBuffer)) != -1) {
1✔
113
                            outputStream.write(readBuffer, 0, readLen);
1✔
114
                        }
115
                    }
116
                }
1✔
117
            }
118
            this.isExtracted = true;
1✔
119
            // register deletion on exit
120
            FileUtils.forceDeleteOnExit(folder);
1✔
NEW
121
        } catch (IOException ex) {
×
NEW
122
            logger.error("Failed to read crate from input stream.", ex);
×
123
        }
1✔
124
    }
1✔
125

126
    @Override
127
    public ObjectNode readMetadataJson(InputStream stream) {
128
        if (!isExtracted) {
1✔
129
            this.readCrate(stream);
1✔
130
        }
131

132
        ObjectMapper objectMapper = MyObjectMapper.getMapper();
1✔
133
        File jsonMetadata = temporaryFolder.resolve("ro-crate-metadata.json").toFile();
1✔
134

135
        try {
136
            return objectMapper.readTree(jsonMetadata).deepCopy();
1✔
NEW
137
        } catch (IOException e) {
×
NEW
138
            logger.error("Failed to deserialize crate metadata.", e);
×
NEW
139
            return null;
×
140
        }
141
    }
142

143
    @Override
144
    public File readContent(InputStream stream) {
145
        if (!isExtracted) {
1✔
NEW
146
            this.readCrate(stream);
×
147
        }
148
        return temporaryFolder.toFile();
1✔
149
    }
150
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc