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.collections4.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.lang3.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 ZipArchiveEntry firstEntry = ((ZipArchiveEntry) first);
123 ZipArchiveEntry secondEntry = ((ZipArchiveEntry) second);
124 if (firstEntry.isDirectory() != secondEntry.isDirectory()) {
125
126 return Boolean.compare(secondEntry.isDirectory(), firstEntry.isDirectory());
127 }
128
129 return firstEntry.getName().compareTo(secondEntry.getName());
130 }
131 });
132
133 final Iterator it = zipEntries.iterator();
134 while (it.hasNext()) {
135 ZipArchiveEntry entry = (ZipArchiveEntry) it.next();
136 processEntry(zip, entry);
137 }
138 context.getJCRSession(getRepository()).save();
139 }
140 return false;
141 }
142
143 private void processEntry(ZipFile zip, ZipArchiveEntry entry) throws IOException, RepositoryException {
144 if (entry.getName().startsWith("__MACOSX")) {
145 return;
146 } else if (entry.getName().endsWith(".DS_Store")) {
147 return;
148 }
149
150 if (entry.isDirectory()) {
151 ensureFolder(entry);
152 } else {
153 handleFileEntry(zip, entry);
154 }
155
156 }
157
158 protected void handleFileEntry(ZipFile zip, ZipArchiveEntry entry) throws IOException, RepositoryException {
159 String fileName = entry.getName();
160 if (StringUtils.contains(fileName, "/")) {
161 fileName = StringUtils.substringAfterLast(fileName, "/");
162 }
163
164 String extension = StringUtils.substringAfterLast(fileName, ".");
165 InputStream stream = zip.getInputStream(entry);
166 FileOutputStream os = null;
167 try {
168 UploadReceiver receiver = createReceiver();
169
170 String folderPath = extractEntryPath(entry);
171 if (folderPath.startsWith("/")) {
172 folderPath = folderPath.substring(1);
173 }
174 Node folder = getJCRNode(context);
175 if (StringUtils.isNotBlank(folderPath)) {
176 if (folder.hasNode(folderPath)) {
177 folder = folder.getNode(folderPath);
178 } else {
179 folder = NodeUtil.createPath(folder, folderPath, NodeTypes.Folder.NAME, true);
180 }
181 }
182 receiver.setFieldType(UploadField.FieldType.BYTE_ARRAY);
183 receiver.receiveUpload(fileName, StringUtils.defaultIfEmpty(MIMEMapping.getMIMEType(extension), DEFAULT_MIME_TYPE));
184 receiver.setValue(IOUtils.toByteArray(stream));
185
186 doHandleEntryFromReceiver(folder, receiver);
187 } catch (Exception e) {
188 throw e;
189 } finally {
190 IOUtils.closeQuietly(stream);
191 IOUtils.closeQuietly(os);
192 }
193 }
194
195 protected UploadReceiver createReceiver() {
196 return new UploadReceiver(Path.getTempDirectory(), translator);
197 }
198
199
200
201
202
203
204
205
206 protected void doHandleEntryFromReceiver(Node folder, UploadReceiver receiver) throws RepositoryException {
207 }
208
209 private void ensureFolder(ZipArchiveEntry entry) throws RepositoryException {
210 String path = extractEntryPath(entry);
211 NodeUtil.createPath(getJCRNode(context), path, NodeTypes.Folder.NAME, true);
212 }
213
214 private String extractEntryPath(ZipArchiveEntry entry) {
215 String entryName = entry.getName();
216 String path = (StringUtils.contains(entryName, "/")) ? StringUtils.substringBeforeLast(entryName, "/") : "/";
217
218 if (!path.startsWith("/")) {
219 path = "/" + path;
220 }
221
222
223 path = StringUtils.replace(path, "/", BACKSLASH_DUMMY);
224 path = Path.getValidatedLabel(path);
225 path = StringUtils.replace(path, BACKSLASH_DUMMY, "/");
226 return path;
227 }
228
229 private boolean isValid(File tmpFile) {
230 return tmpFile != null;
231 }
232
233
234 public InputStream getInputStream() {
235 return inputStream;
236 }
237
238 public void setInputStream(InputStream inputStream) {
239 this.inputStream = inputStream;
240 }
241
242 public String getEncoding() {
243 return encoding;
244 }
245
246 public void setEncoding(String encoding) {
247 this.encoding = encoding;
248 }
249 }