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.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 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 = createReceiver();
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 protected UploadReceiver createReceiver() {
193 return new UploadReceiver(Path.getTempDirectory(), translator);
194 }
195
196
197
198
199
200
201
202
203 protected void doHandleEntryFromReceiver(Node folder, UploadReceiver receiver) throws RepositoryException {}
204
205 private void ensureFolder(ZipArchiveEntry entry) throws RepositoryException {
206 String path = extractEntryPath(entry);
207 NodeUtil.createPath(getJCRNode(context), path, NodeTypes.Folder.NAME, true);
208 }
209
210 private String extractEntryPath(ZipArchiveEntry entry) {
211 String entryName = entry.getName();
212 String path = (StringUtils.contains(entryName, "/")) ? StringUtils.substringBeforeLast(entryName, "/") : "/";
213
214 if (!path.startsWith("/")) {
215 path = "/" + path;
216 }
217
218
219 path = StringUtils.replace(path, "/", BACKSLASH_DUMMY);
220 path = Path.getValidatedLabel(path);
221 path = StringUtils.replace(path, BACKSLASH_DUMMY, "/");
222 return path;
223 }
224
225 private boolean isValid(File tmpFile) {
226 return tmpFile != null;
227 }
228
229
230 public InputStream getInputStream() {
231 return inputStream;
232 }
233
234 public void setInputStream(InputStream inputStream) {
235 this.inputStream = inputStream;
236 }
237
238 public String getEncoding() {
239 return encoding;
240 }
241
242 public void setEncoding(String encoding) {
243 this.encoding = encoding;
244 }
245 }