1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34 package info.magnolia.ui.framework.command;
35
36 import info.magnolia.cms.beans.config.MIMEMapping;
37 import info.magnolia.cms.core.Path;
38 import info.magnolia.commands.impl.BaseRepositoryCommand;
39 import info.magnolia.context.Context;
40 import info.magnolia.i18nsystem.SimpleTranslator;
41 import info.magnolia.jcr.util.NodeTypes;
42 import info.magnolia.jcr.util.NodeUtil;
43 import info.magnolia.ui.form.field.upload.UploadReceiver;
44
45 import java.io.File;
46 import java.io.FileOutputStream;
47 import java.io.IOException;
48 import java.io.InputStream;
49 import java.util.Collections;
50 import java.util.Comparator;
51 import java.util.Iterator;
52 import java.util.List;
53
54 import javax.inject.Inject;
55 import javax.jcr.Node;
56 import javax.jcr.RepositoryException;
57
58 import org.apache.commons.collections.EnumerationUtils;
59 import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
60 import org.apache.commons.compress.archivers.zip.ZipFile;
61 import org.apache.commons.io.IOUtils;
62 import org.apache.commons.lang.StringUtils;
63 import org.vaadin.easyuploads.UploadField;
64
65
66
67
68 public class ImportZipCommand extends BaseRepositoryCommand {
69
70 public static final String STREAM_PROPERTY = "inputStream";
71
72 public static final String ENCODING_PROPERTY = "encoding";
73
74 public static final String ZIP_TMP_FILE_PREFIX = "zipupload";
75
76 public static final String ZIP_TMP_FILE_SUFFIX = ".zip";
77
78 public static final String DEFAULT_MIME_TYPE = "application/octet-stream";
79
80 public static final String BACKSLASH_DUMMY = "________backslash________";
81
82 private SimpleTranslator translator;
83
84 private InputStream inputStream;
85
86 protected Context context;
87
88 private String encoding;
89
90 @Inject
91 public ImportZipCommand(SimpleTranslator translator) {
92 this.translator = translator;
93 }
94
95 @Override
96 public boolean execute(Context context) throws Exception {
97 this.context = context;
98 File tmpFile = null;
99 FileOutputStream tmpStream = null;
100 try {
101 tmpFile = File.createTempFile(ZIP_TMP_FILE_PREFIX, ZIP_TMP_FILE_SUFFIX);
102 tmpStream = new FileOutputStream(tmpFile);
103 IOUtils.copy(inputStream, tmpStream);
104 } catch (IOException e) {
105 log.error("Failed to dump zip file to temp file: ", e);
106 throw e;
107 } finally {
108 IOUtils.closeQuietly(tmpStream);
109 IOUtils.closeQuietly(inputStream);
110 }
111
112 if (isValid(tmpFile)) {
113 ZipFile zip = new ZipFile(tmpFile, getEncoding());
114
115
116
117
118 final List zipEntries = EnumerationUtils.toList(zip.getEntries());
119 Collections.sort(zipEntries, new Comparator() {
120 @Override
121 public int compare(Object first, Object second) {
122 Boolean isFirstDirectory = ((ZipArchiveEntry)first).isDirectory();
123 Boolean isSecondDirectory = ((ZipArchiveEntry)second).isDirectory();
124 return isFirstDirectory.compareTo(isSecondDirectory);
125 }
126 });
127
128 Collections.reverse(zipEntries);
129 final Iterator it = zipEntries.iterator();
130 while (it.hasNext()) {
131 ZipArchiveEntry entry = (ZipArchiveEntry) it.next();
132 processEntry(zip, entry);
133 }
134 context.getJCRSession(getRepository()).save();
135 }
136 return false;
137 }
138
139 private void processEntry(ZipFile zip, ZipArchiveEntry entry) throws IOException, RepositoryException {
140 if (entry.getName().startsWith("__MACOSX")) {
141 return;
142 }
143 else if (entry.getName().endsWith(".DS_Store")) {
144 return;
145 }
146
147 if (entry.isDirectory()) {
148 ensureFolder(entry);
149 } else {
150 handleFileEntry(zip, entry);
151 }
152
153 }
154
155 protected void handleFileEntry(ZipFile zip, ZipArchiveEntry entry) throws IOException, RepositoryException {
156 String fileName = entry.getName();
157 if (StringUtils.contains(fileName, "/")) {
158 fileName = StringUtils.substringAfterLast(fileName, "/");
159 }
160
161 String extension = StringUtils.substringAfterLast(fileName, ".");
162 InputStream stream = zip.getInputStream(entry);
163 FileOutputStream os = null;
164 try {
165 UploadReceiver receiver = new UploadReceiver(Path.getTempDirectory(), translator);
166
167 String folderPath = extractEntryPath(entry);
168 if (folderPath.startsWith("/")) {
169 folderPath = folderPath.substring(1);
170 }
171 Node folder = getJCRNode(context);
172 if (StringUtils.isNotBlank(folderPath)) {
173 if (folder.hasNode(folderPath)) {
174 folder = folder.getNode(folderPath);
175 } else {
176 folder = NodeUtil.createPath(folder, folderPath, NodeTypes.Folder.NAME, true);
177 }
178 }
179 receiver.setFieldType(UploadField.FieldType.BYTE_ARRAY);
180 receiver.receiveUpload(fileName, StringUtils.defaultIfEmpty(MIMEMapping.getMIMEType(extension), DEFAULT_MIME_TYPE));
181 receiver.setValue(IOUtils.toByteArray(stream));
182
183 doHandleEntryFromReceiver(folder, receiver);
184 } catch (IOException e) {
185 throw e;
186 } finally {
187 IOUtils.closeQuietly(stream);
188 IOUtils.closeQuietly(os);
189 }
190 }
191
192
193
194
195
196
197
198
199 protected void doHandleEntryFromReceiver(Node folder, UploadReceiver receiver) throws RepositoryException {}
200
201 private void ensureFolder(ZipArchiveEntry entry) throws RepositoryException {
202 String path = extractEntryPath(entry);
203 NodeUtil.createPath(getJCRNode(context), path, NodeTypes.Folder.NAME, true);
204 }
205
206 private String extractEntryPath(ZipArchiveEntry entry) {
207 String entryName = entry.getName();
208 String path = (StringUtils.contains(entryName, "/")) ? StringUtils.substringBeforeLast(entryName, "/") : "/";
209
210 if (!path.startsWith("/")) {
211 path = "/" + path;
212 }
213
214
215 path = StringUtils.replace(path, "/", BACKSLASH_DUMMY);
216 path = Path.getValidatedLabel(path);
217 path = StringUtils.replace(path, BACKSLASH_DUMMY, "/");
218 return path;
219 }
220
221 private boolean isValid(File tmpFile) {
222 return tmpFile != null;
223 }
224
225
226 public InputStream getInputStream() {
227 return inputStream;
228 }
229
230 public void setInputStream(InputStream inputStream) {
231 this.inputStream = inputStream;
232 }
233
234 public String getEncoding() {
235 return encoding;
236 }
237
238 public void setEncoding(String encoding) {
239 this.encoding = encoding;
240 }
241 }