Skip to content
This repository was archived by the owner on Nov 11, 2022. It is now read-only.

Commit adadb61

Browse files
davorbonacilukecwik
authored andcommitted
Dataflow on Windows: fix an issue where the ZIP archive would contain file names with a '\' (backslash) on Windows. Using '/' (slash) is compatible with most platforms.
[] ------------- Created by MOE: http://code.google.com/p/moe-java MOE_MIGRATED_REVID=83623330
1 parent 7598e29 commit adadb61

2 files changed

Lines changed: 68 additions & 25 deletions

File tree

sdk/src/main/java/com/google/cloud/dataflow/sdk/util/PackageUtil.java

Lines changed: 63 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@
1616

1717
package com.google.cloud.dataflow.sdk.util;
1818

19+
import static com.google.common.base.Preconditions.checkArgument;
20+
import static com.google.common.base.Preconditions.checkNotNull;
21+
1922
import com.google.api.client.util.BackOff;
2023
import com.google.api.client.util.BackOffUtils;
2124
import com.google.api.client.util.Sleeper;
@@ -36,6 +39,7 @@
3639

3740
import java.io.File;
3841
import java.io.IOException;
42+
import java.io.OutputStream;
3943
import java.nio.channels.Channels;
4044
import java.nio.channels.WritableByteChannel;
4145
import java.nio.charset.StandardCharsets;
@@ -253,46 +257,84 @@ private static String computeContentHash(File classpathElement) throws IOExcepti
253257
private static void copyContent(String classpathElement, WritableByteChannel outputChannel)
254258
throws IOException {
255259
final File classpathElementFile = new File(classpathElement);
256-
if (!classpathElementFile.isDirectory()) {
260+
if (classpathElementFile.isDirectory()) {
261+
zipDirectory(classpathElementFile, Channels.newOutputStream(outputChannel));
262+
} else {
257263
Files.asByteSource(classpathElementFile).copyTo(Channels.newOutputStream(outputChannel));
258-
return;
259264
}
265+
}
260266

261-
ZipOutputStream zos = new ZipOutputStream(Channels.newOutputStream(outputChannel));
262-
zipDirectoryRecursive(classpathElementFile, classpathElementFile, zos);
267+
/**
268+
* Zips an entire directory specified by the path.
269+
*
270+
* @param sourceDirectory the directory to read from. This directory and all
271+
* subdirectories will be added to the zip-file. The path within the zip
272+
* file is relative to the directory given as parameter, not absolute.
273+
* @param outputStream the stream to write the zip-file to. This method does not close
274+
* outputStream.
275+
* @throws IOException the zipping failed, e.g. because the input was not
276+
* readable.
277+
*/
278+
private static void zipDirectory(
279+
File sourceDirectory,
280+
OutputStream outputStream) throws IOException {
281+
checkNotNull(sourceDirectory);
282+
checkNotNull(outputStream);
283+
checkArgument(
284+
sourceDirectory.isDirectory(),
285+
"%s is not a valid directory",
286+
sourceDirectory.getAbsolutePath());
287+
ZipOutputStream zos = new ZipOutputStream(outputStream);
288+
for (File file : sourceDirectory.listFiles()) {
289+
zipDirectoryInternal(file, "", zos);
290+
}
263291
zos.finish();
264292
}
265293

266294
/**
267-
* Private helper function for zipping files. This one goes recursively through the input
268-
* directory and all of its subdirectories and adds the single zip entries.
295+
* Private helper function for zipping files. This one goes recursively
296+
* through the input directory and all of its subdirectories and adds the
297+
* single zip entries.
269298
*
270-
* @param file the file or directory to be added to the zip file.
271-
* @param root each file uses the root directory to generate its relative path within the zip.
272-
* @param zos the zipstream to write to.
273-
* @throws IOException the zipping failed, e.g. because the output was not writable.
299+
* @param inputFile the file or directory to be added to the zip file
300+
* @param directoryName the string-representation of the parent directory
301+
* name. Might be an empty name, or a name containing multiple directory
302+
* names separated by "/". The directory name must be a valid name
303+
* according to the file system limitations.
304+
* @param zos the zipstream to write to
305+
* @throws IOException the zipping failed, e.g. because the output was not
306+
* writeable.
274307
*/
275-
private static void zipDirectoryRecursive(File file, File root, ZipOutputStream zos)
276-
throws IOException {
277-
final String entryName = relativize(file, root);
278-
if (file.isDirectory()) {
279-
// We are hitting a directory. Start the recursion.
280-
// Add the empty entry if it is a subdirectory and the subdirectory has no children.
281-
// Don't add it otherwise, as this is incompatible with certain implementations of unzip.
282-
if (file.list().length == 0 && !file.equals(root)) {
308+
private static void zipDirectoryInternal(
309+
File inputFile,
310+
String directoryName,
311+
ZipOutputStream zos) throws IOException {
312+
final String entryName;
313+
if ("".equals(directoryName)) {
314+
// no parent directories yet.
315+
entryName = inputFile.getName();
316+
} else {
317+
entryName = directoryName + "/" + inputFile.getName();
318+
}
319+
if (inputFile.isDirectory()) {
320+
// We are hitting a sub-directory. Start the recursion.
321+
// Add the empty entry for a subdirectory if we have no children files.
322+
// Don't add it if we have them, as this is incompatible with certain
323+
// implementations of unzip.
324+
if (inputFile.list().length == 0) {
283325
ZipEntry entry = new ZipEntry(entryName + "/");
284326
zos.putNextEntry(entry);
285327
} else {
286328
// loop through the directory content, and zip the files
287-
for (File currentFile : file.listFiles()) {
288-
zipDirectoryRecursive(currentFile, root, zos);
329+
for (File file : inputFile.listFiles()) {
330+
zipDirectoryInternal(file, entryName, zos);
289331
}
290332
}
291333
} else {
292334
// Put the next zip-entry into the zipoutputstream.
293335
ZipEntry entry = new ZipEntry(entryName);
294336
zos.putNextEntry(entry);
295-
Files.asByteSource(file).copyTo(zos);
337+
Files.asByteSource(inputFile).copyTo(zos);
296338
}
297339
}
298340

sdk/src/test/java/com/google/cloud/dataflow/sdk/util/PackageUtilTest.java

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,11 @@
1616

1717
package com.google.cloud.dataflow.sdk.util;
1818

19+
import static org.hamcrest.collection.IsIterableContainingInAnyOrder.containsInAnyOrder;
1920
import static org.junit.Assert.assertEquals;
2021
import static org.junit.Assert.assertFalse;
2122
import static org.junit.Assert.assertNull;
22-
import static org.junit.Assert.assertTrue;
23+
import static org.junit.Assert.assertThat;
2324
import static org.mockito.Matchers.any;
2425
import static org.mockito.Matchers.anyString;
2526
import static org.mockito.Mockito.times;
@@ -35,7 +36,6 @@
3536
import com.google.common.io.Files;
3637
import com.google.common.io.LineReader;
3738

38-
import org.hamcrest.CoreMatchers;
3939
import org.junit.Before;
4040
import org.junit.Rule;
4141
import org.junit.Test;
@@ -210,8 +210,9 @@ public void testPackageUploadWithDirectorySucceeds() throws Exception {
210210
entry = inputStream.getNextEntry()) {
211211
zipEntryNames.add(entry.getName());
212212
}
213-
assertTrue(CoreMatchers.hasItems("directory/file.txt", "empty_directory/", "file.txt").matches(
214-
zipEntryNames));
213+
214+
assertThat(zipEntryNames,
215+
containsInAnyOrder("directory/file.txt", "empty_directory/", "file.txt"));
215216
}
216217

217218
@Test

0 commit comments

Comments
 (0)