diff --git a/common/src/main/java/com/genexus/util/GXExternalFileInfo.java b/common/src/main/java/com/genexus/util/GXExternalFileInfo.java index 7da9a5792..0be4657ad 100644 --- a/common/src/main/java/com/genexus/util/GXExternalFileInfo.java +++ b/common/src/main/java/com/genexus/util/GXExternalFileInfo.java @@ -8,6 +8,7 @@ import com.genexus.CommonUtil; import com.genexus.db.driver.*; import com.genexus.common.interfaces.SpecificImplementation; +import com.genexus.db.driver.ResourceAccessControlList; import java.util.Date; diff --git a/common/src/main/java/com/genexus/util/GxFileInfoSourceType.java b/common/src/main/java/com/genexus/util/GxFileInfoSourceType.java new file mode 100644 index 000000000..1988b416d --- /dev/null +++ b/common/src/main/java/com/genexus/util/GxFileInfoSourceType.java @@ -0,0 +1,7 @@ +package com.genexus.util; + +public enum GxFileInfoSourceType { + Unknown, + LocalFile, + ExternalFile +} diff --git a/common/src/main/java/com/genexus/db/driver/ResourceAccessControlList.java b/common/src/main/java/com/genexus/util/ResourceAccessControlList.java similarity index 100% rename from common/src/main/java/com/genexus/db/driver/ResourceAccessControlList.java rename to common/src/main/java/com/genexus/util/ResourceAccessControlList.java diff --git a/gxoffice/src/main/java/com/genexus/gxoffice/poi/sxssf/ExcelDocument.java b/gxoffice/src/main/java/com/genexus/gxoffice/poi/sxssf/ExcelDocument.java index e3329ab91..3bd03131f 100644 --- a/gxoffice/src/main/java/com/genexus/gxoffice/poi/sxssf/ExcelDocument.java +++ b/gxoffice/src/main/java/com/genexus/gxoffice/poi/sxssf/ExcelDocument.java @@ -1,5 +1,6 @@ package com.genexus.gxoffice.poi.sxssf; +import com.genexus.util.GxFileInfoSourceType; import org.apache.poi.xssf.streaming.SXSSFWorkbook; import org.apache.poi.xssf.usermodel.XSSFWorkbook; @@ -28,8 +29,7 @@ public short Open(String fileName) { return errCod; } } else { - boolean isAbsolute = new java.io.File(fileName).isAbsolute(); - GXFile file = new GXFile(fileName, Constants.EXTERNAL_UPLOAD_ACL, isAbsolute); + GXFile file = new GXFile("", fileName, Constants.EXTERNAL_UPLOAD_ACL, GxFileInfoSourceType.Unknown); if (file.exists()) { // System.out.println("Opening.."); workBook = new SXSSFWorkbook(new XSSFWorkbook(file.getStream())); diff --git a/gxoffice/src/main/java/com/genexus/gxoffice/poi/xssf/ExcelDocument.java b/gxoffice/src/main/java/com/genexus/gxoffice/poi/xssf/ExcelDocument.java index 050879141..abfd3aa25 100644 --- a/gxoffice/src/main/java/com/genexus/gxoffice/poi/xssf/ExcelDocument.java +++ b/gxoffice/src/main/java/com/genexus/gxoffice/poi/xssf/ExcelDocument.java @@ -3,6 +3,7 @@ import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; +import com.genexus.util.GxFileInfoSourceType; import org.apache.poi.ss.usermodel.Row; import org.apache.poi.ss.usermodel.Sheet; import org.apache.poi.ss.usermodel.Workbook; @@ -36,8 +37,7 @@ public short Open(String fileName) { return errCod; } } else { - boolean isAbsolute = new java.io.File(fileName).isAbsolute(); - GXFile file = new GXFile(fileName, Constants.EXTERNAL_UPLOAD_ACL, isAbsolute); + GXFile file = new GXFile("", fileName, Constants.EXTERNAL_UPLOAD_ACL, GxFileInfoSourceType.Unknown); if (file.exists()) { // System.out.println("Opening.."); workBook = new XSSFWorkbook(file.getStream()); diff --git a/java/src/main/java/com/genexus/GxImageUtil.java b/java/src/main/java/com/genexus/GxImageUtil.java index 4c838ce52..297171ed8 100644 --- a/java/src/main/java/com/genexus/GxImageUtil.java +++ b/java/src/main/java/com/genexus/GxImageUtil.java @@ -5,99 +5,146 @@ import java.awt.geom.AffineTransform; import java.awt.image.AffineTransformOp; import java.awt.image.BufferedImage; -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; +import java.io.*; +import com.genexus.db.driver.ResourceAccessControlList; +import com.genexus.util.GxFileInfoSourceType; import com.genexus.util.GXFile; import org.apache.logging.log4j.Logger; public class GxImageUtil { private static Logger log = org.apache.logging.log4j.LogManager.getLogger(GxImageUtil.class); + private static int INVALID_CODE = -1; - private static String getImageAbsolutePath(String imageFile){ - if (CommonUtil.isUploadPrefix(imageFile)) { - return new GXFile(imageFile).getAbsolutePath(); + private static InputStream getInputStream(String filePathOrUrl) throws IOException { + return getGXFile(filePathOrUrl).getStream(); + } + + private static BufferedImage createBufferedImageFromURI(String filePathOrUrl) throws IOException + { + try (InputStream is = getGXFile(filePathOrUrl).getStream()) { + return ImageIO.read(is); + } + catch (IOException e) { + log.error("Failed to read image stream: " + filePathOrUrl); + throw e; } - String defaultPath = com.genexus.ModelContext.getModelContext().getHttpContext().getDefaultPath(); - return imageFile.startsWith(defaultPath)? imageFile : defaultPath + imageFile.replace("/", File.separator); + } + + private static GXFile getGXFile(String filePathOrUrl) { + String basePath = (com.genexus.ModelContext.getModelContext() != null) ? com.genexus.ModelContext.getModelContext().getHttpContext().getDefaultPath(): ""; + return new GXFile(basePath, filePathOrUrl, ResourceAccessControlList.Default, GxFileInfoSourceType.Unknown); } public static long getFileSize(String imageFile){ - return new File(getImageAbsolutePath(imageFile)).length(); + if (!isValidInput(imageFile)) + return INVALID_CODE; + + return new GXFile(imageFile).getLength(); } public static int getImageHeight(String imageFile) { + if (!isValidInput(imageFile)) + return INVALID_CODE; + try { - BufferedImage image = ImageIO.read(new File(getImageAbsolutePath(imageFile))); - return image.getHeight(); + return createBufferedImageFromURI(imageFile).getHeight(); } - catch (IOException e) { + catch (Exception e) { log.error("getImageHeight " + imageFile + " failed" , e); - return 0; } + return INVALID_CODE; + } + + private static boolean isValidInput(String imageFile) { + boolean isValid = imageFile != null && imageFile.length() > 0; + if (!isValid) { + log.debug("Image Api - FileName cannot be empty"); + } + return isValid; } public static int getImageWidth(String imageFile) { + if (!isValidInput(imageFile)) + return INVALID_CODE; + try { - BufferedImage image = ImageIO.read(new File(getImageAbsolutePath(imageFile))); - return image.getWidth(); + return createBufferedImageFromURI(imageFile).getWidth(); } - catch (IOException e) { + catch (Exception e) { log.error("getImageWidth " + imageFile + " failed" , e); - return 0; } + return INVALID_CODE; } - public static String crop(String imageFile, int x, int y, int width, int height){ + public static String crop(String imageFile, int x, int y, int width, int height) { + if (!isValidInput(imageFile)) + return ""; + try { - String absolutePath = getImageAbsolutePath(imageFile); - BufferedImage image = ImageIO.read(new File(absolutePath)); - BufferedImage cropedImage = image.getSubimage(x, y, width, height); - ImageIO.write(cropedImage, CommonUtil.getFileType(absolutePath), new FileOutputStream(absolutePath)); + BufferedImage image = createBufferedImageFromURI(imageFile); + BufferedImage croppedImage = image.getSubimage(x, y, width, height); + writeImage(croppedImage, imageFile); } - catch (IOException e) { + catch (Exception e) { log.error("crop " + imageFile + " failed" , e); } return imageFile; } - public static String flipHorizontally(String imageFile){ + private static void writeImage(BufferedImage croppedImage, String destinationFilePathOrUrl) throws IOException { + try (ByteArrayOutputStream outStream = new ByteArrayOutputStream()) { + ImageIO.write(croppedImage, CommonUtil.getFileType(destinationFilePathOrUrl), outStream); + try (ByteArrayInputStream inStream = new ByteArrayInputStream(outStream.toByteArray())) { + GXFile file = getGXFile(destinationFilePathOrUrl); + file.create(inStream, true); + file.close(); + } + } + } + + public static String flipHorizontally(String imageFile) { + if (!isValidInput(imageFile)) + return ""; + try { - String absolutePath = getImageAbsolutePath(imageFile); - BufferedImage image = ImageIO.read(new File(absolutePath)); + BufferedImage image = createBufferedImageFromURI(imageFile); AffineTransform tx = AffineTransform.getScaleInstance(-1, 1); tx.translate(-image.getWidth(null), 0); AffineTransformOp op = new AffineTransformOp(tx, AffineTransformOp.TYPE_NEAREST_NEIGHBOR); - BufferedImage flipedImage = op.filter(image, null); - ImageIO.write(flipedImage, CommonUtil.getFileType(absolutePath), new FileOutputStream(absolutePath)); + BufferedImage flippedImage = op.filter(image, null); + writeImage(flippedImage, imageFile); } - catch (IOException e) { + catch (Exception e) { log.error("flip horizontal " + imageFile + " failed" , e); } return imageFile; } - public static String flipVertically(String imageFile){ + public static String flipVertically(String imageFile) { + if (!isValidInput(imageFile)) + return ""; + try { - String absolutePath = getImageAbsolutePath(imageFile); - BufferedImage image = ImageIO.read(new File(absolutePath)); + BufferedImage image = createBufferedImageFromURI(imageFile); AffineTransform tx = AffineTransform.getScaleInstance(1, -1); tx.translate(0, -image.getHeight(null)); AffineTransformOp op = new AffineTransformOp(tx, AffineTransformOp.TYPE_NEAREST_NEIGHBOR); - BufferedImage flipedImage = op.filter(image, null); - ImageIO.write(flipedImage, CommonUtil.getFileType(absolutePath), new FileOutputStream(absolutePath)); + BufferedImage flippedImage = op.filter(image, null); + writeImage(flippedImage, imageFile); } - catch (IOException e) { + catch (Exception e) { log.error("flip vertical " + imageFile + " failed" , e); } return imageFile; } - public static String resize(String imageFile, int width, int height, boolean keepAspectRatio){ + public static String resize(String imageFile, int width, int height, boolean keepAspectRatio) { + if (!isValidInput(imageFile)) + return ""; + try { - String absolutePath = getImageAbsolutePath(imageFile); - BufferedImage image = ImageIO.read(new File(absolutePath)); + BufferedImage image = createBufferedImageFromURI(imageFile); if (keepAspectRatio) { double imageHeight = image.getHeight(); double imageWidth = image.getWidth(); @@ -112,34 +159,37 @@ public static String resize(String imageFile, int width, int height, boolean kee Graphics2D g2d = resizedImage.createGraphics(); g2d.drawImage(image, 0, 0, width, height, null); g2d.dispose(); - ImageIO.write(resizedImage, CommonUtil.getFileType(absolutePath), new FileOutputStream(absolutePath)); + writeImage(resizedImage, imageFile); } - catch (IOException e) { + catch (Exception e) { log.error("resize " + imageFile + " failed" , e); } return imageFile; } - public static String scale(String imageFile, short percent){ + public static String scale(String imageFile, short percent) { + if (!isValidInput(imageFile)) + return ""; + try { - String absolutePath = getImageAbsolutePath(imageFile); - BufferedImage image = ImageIO.read(new File(absolutePath)); + BufferedImage image = createBufferedImageFromURI(imageFile); imageFile = resize(imageFile, image.getWidth() * percent / 100, image.getHeight() * percent / 100,true); } - catch (IOException e) { + catch (Exception e) { log.error("scale " + imageFile + " failed" , e); } return imageFile; } - public static String rotate(String imageFile, short angle){ + public static String rotate(String imageFile, short angle) { + if (!isValidInput(imageFile)) + return ""; try { - String absolutePath = getImageAbsolutePath(imageFile); - BufferedImage image = ImageIO.read(new File(absolutePath)); + BufferedImage image = createBufferedImageFromURI(imageFile); BufferedImage rotatedImage = rotateImage(image, angle); - ImageIO.write(rotatedImage, CommonUtil.getFileType(absolutePath), new FileOutputStream(absolutePath)); + writeImage(rotatedImage, imageFile); } - catch (IOException e) { + catch (Exception e) { log.error("rotate " + imageFile + " failed" , e); } return imageFile; diff --git a/java/src/main/java/com/genexus/PrivateUtilities.java b/java/src/main/java/com/genexus/PrivateUtilities.java index 7118615c5..fb7ef791f 100644 --- a/java/src/main/java/com/genexus/PrivateUtilities.java +++ b/java/src/main/java/com/genexus/PrivateUtilities.java @@ -19,6 +19,8 @@ import java.lang.reflect.Field; import java.net.HttpURLConnection; import java.net.URL; +import java.nio.file.InvalidPathException; +import java.nio.file.Paths; import java.util.Properties; import java.util.Random; import java.util.zip.DeflaterOutputStream; @@ -609,6 +611,20 @@ private static File parent(File f) { return new File(dirname); } + /** + *
+	 * Checks if a string is an absolute path to local file system.
+	 * Null safe.
+	 * 
+ */ + public static boolean isAbsoluteFilePath(String path) { + try { + return Paths.get(path).isAbsolute(); + } catch (InvalidPathException | NullPointerException ex) { + return false; + } + } + public static String addLastPathSeparator(String dir) { return addLastChar(dir, File.separator); diff --git a/java/src/main/java/com/genexus/db/driver/GXResultSet.java b/java/src/main/java/com/genexus/db/driver/GXResultSet.java index 06788c07b..1876b69c5 100644 --- a/java/src/main/java/com/genexus/db/driver/GXResultSet.java +++ b/java/src/main/java/com/genexus/db/driver/GXResultSet.java @@ -29,6 +29,7 @@ import com.genexus.internet.HttpContext; import com.genexus.util.GXFile; import com.genexus.util.GXServices; +import com.genexus.db.driver.ResourceAccessControlList; public final class GXResultSet implements ResultSet, com.genexus.db.IFieldGetter, IGXResultSet { diff --git a/java/src/main/java/com/genexus/specific/java/FileUtils.java b/java/src/main/java/com/genexus/specific/java/FileUtils.java index 3868330e7..40e72a319 100644 --- a/java/src/main/java/com/genexus/specific/java/FileUtils.java +++ b/java/src/main/java/com/genexus/specific/java/FileUtils.java @@ -1,8 +1,9 @@ package com.genexus.specific.java; import com.genexus.common.classes.AbstractGXFile; -import com.genexus.util.GXFile; import com.genexus.db.driver.ResourceAccessControlList; +import com.genexus.util.GXFile; +import com.genexus.util.GxFileInfoSourceType; import java.io.File; import java.io.IOException; @@ -47,7 +48,7 @@ public void copyFile(File file, File file2) throws IOException { @Override public AbstractGXFile createFile(String file, ResourceAccessControlList acl, boolean local) { - return new GXFile(file, acl, local); + return new GXFile(file, acl, local ? GxFileInfoSourceType.LocalFile: GxFileInfoSourceType.Unknown); } } diff --git a/java/src/main/java/com/genexus/util/GXFile.java b/java/src/main/java/com/genexus/util/GXFile.java index 641897eaf..921439e35 100644 --- a/java/src/main/java/com/genexus/util/GXFile.java +++ b/java/src/main/java/com/genexus/util/GXFile.java @@ -4,13 +4,13 @@ import java.io.IOException; import java.io.InputStream; import java.net.URI; +import java.nio.file.Paths; import java.util.Date; import java.util.GregorianCalendar; import java.util.Iterator; import java.util.Vector; -import com.genexus.IHttpContext; -import com.genexus.ModelContext; +import com.genexus.*; import com.genexus.common.interfaces.SpecificImplementation; import com.genexus.db.driver.ResourceAccessControlList; import com.genexus.db.driver.ExternalProvider; @@ -19,8 +19,6 @@ import org.apache.commons.io.LineIterator; import org.apache.commons.io.output.FileWriterWithEncoding; -import com.genexus.Application; -import com.genexus.CommonUtil; import com.genexus.common.classes.AbstractGXFile; import org.apache.logging.log4j.Logger; @@ -45,34 +43,61 @@ public GXFile(String fileName) { } //For compatibility reasons + @Deprecated public GXFile(String fileName, boolean isPrivate) { - this(fileName, isPrivate ? ResourceAccessControlList.Private: ResourceAccessControlList.Default, false); + this(fileName, isPrivate ? ResourceAccessControlList.Private: ResourceAccessControlList.Default, GxFileInfoSourceType.Unknown); } public GXFile(String fileName, ResourceAccessControlList fileAcl) { - this(fileName, fileAcl, false); + this(fileName, fileAcl, GxFileInfoSourceType.Unknown); } - public GXFile(String fileName, ResourceAccessControlList fileAcl, boolean isLocal) { + public GXFile(String fileName, ResourceAccessControlList fileAcl, GxFileInfoSourceType sourceType) { + this("", fileName, fileAcl, sourceType); + } + + @Deprecated + public GXFile(String fileName, ResourceAccessControlList fileAcl, boolean isLocalFile) { + this("", fileName, fileAcl, isLocalFile ? GxFileInfoSourceType.LocalFile: GxFileInfoSourceType.Unknown); + } + + public GXFile(String baseDirectoryPath, String fileName, ResourceAccessControlList fileAcl, GxFileInfoSourceType sourceType) { if (com.genexus.CommonUtil.isUploadPrefix(fileName)) { uploadFileId = fileName; fileName = SpecificImplementation.GXutil.getUploadValue(fileName); } - ExternalProvider storageProvider = Application.getExternalProvider(); - if (storageProvider != null && !isLocal) { - FileSource = new GXExternalFileInfo(fileName, storageProvider, true, fileAcl); - } else { - FileSource = new GXFileInfo(new File(fileName)); - } - } + switch (sourceType) { + case LocalFile: + createFileSourceLocal(baseDirectoryPath, fileName); + break; + case ExternalFile: + FileSource = new GXExternalFileInfo(fileName, Application.getExternalProvider(), true, fileAcl); + break; + case Unknown: + ExternalProvider storageProvider = Application.getExternalProvider(); + if (storageProvider == null || PrivateUtilities.isAbsoluteFilePath(fileName)) { + createFileSourceLocal(baseDirectoryPath, fileName); + } + else { + FileSource = new GXExternalFileInfo(fileName, storageProvider, true, fileAcl); + } + break; + } + } + + private void createFileSourceLocal(String baseDirectoryPath, String fileName) { + boolean isAbsolutePath = PrivateUtilities.isAbsoluteFilePath(fileName); + String absoluteOrRelativePath = (isAbsolutePath)? fileName: Paths.get(baseDirectoryPath, fileName).toString(); //BaseDirectory could be empty. + FileSource = new GXFileInfo(new File(absoluteOrRelativePath)); + } - public GXFile(IGXFileInfo fileInfo) { + public GXFile(IGXFileInfo fileInfo) { FileSource = fileInfo; } public static String getgxFilename(String fileName) { - return new GXFile(fileName, ResourceAccessControlList.Default, true).getNameNoExt(); + return new GXFile(fileName, ResourceAccessControlList.Default, GxFileInfoSourceType.LocalFile).getNameNoExt(); } public static String getgxFileext(String fileName) { @@ -774,3 +799,5 @@ public void close() { } } } + + diff --git a/java/src/test/java/com/genexus/TestGxImageUtil.java b/java/src/test/java/com/genexus/TestGxImageUtil.java new file mode 100644 index 000000000..ac607c367 --- /dev/null +++ b/java/src/test/java/com/genexus/TestGxImageUtil.java @@ -0,0 +1,138 @@ +package com.genexus; + +import com.genexus.specific.java.Connect; +import com.genexus.specific.java.LogManager; +import org.junit.Assert; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; + +import java.io.File; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; + +public class TestGxImageUtil { + + private String FILE_NAME = "bird-thumbnail.jpg"; + private String FILE_NAME_COPY = "bird-thumbnail-%s-%s.jpg"; + + private int IMAGE_HEIGHT = 900; + private int IMAGE_WIDTH = 720; + + @Rule + public TemporaryFolder tempFolder = new TemporaryFolder(); + + @Test + public void testImageWidth() + { + int imageWidth = GxImageUtil.getImageWidth(initialize("imageWidth")); + Assert.assertEquals(IMAGE_WIDTH, imageWidth); + } + + private String initialize(String name) + { + Connect.init(); + LogManager.initialize("."); + + String copiedFileName = String.format(FILE_NAME_COPY, name, java.util.UUID.randomUUID().toString()); + File resourcesDirectory = new File("src/test/resources"); + Path originalFileLocation = Paths.get(resourcesDirectory.getPath(), FILE_NAME); + Path copyFileLocation = Paths.get(tempFolder.getRoot().getPath(), copiedFileName); + + try { + Files.copy(originalFileLocation, copyFileLocation); + } + catch (Exception e) { + e.printStackTrace(); + } + + return copyFileLocation.toString(); + } + + @Test + public void testImageHeight() + { + String fileName = initialize("testHeight"); + int imageHeight = GxImageUtil.getImageHeight(fileName); + Assert.assertEquals(IMAGE_HEIGHT, imageHeight); + } + + @Test + public void testImageScale() + { + String fileName = initialize("scaled"); + short scale = 50; + + String imagePath = GxImageUtil.scale(fileName, scale); + + int imageHeight = GxImageUtil.getImageHeight(imagePath); + Assert.assertEquals(IMAGE_HEIGHT * scale / 100, imageHeight); + + int imageWidth = GxImageUtil.getImageWidth(imagePath); + Assert.assertEquals(IMAGE_WIDTH * scale / 100, imageWidth); + } + + @Test + public void testImageCrop() + { + String fileName = initialize("croped"); + String imagePath = GxImageUtil.crop(fileName, 10, 10, 300, 400); + + int imageHeight = GxImageUtil.getImageHeight(imagePath); + Assert.assertEquals(400, imageHeight); + + int imageWidth = GxImageUtil.getImageWidth(imagePath); + Assert.assertEquals(300, imageWidth); + } + + + @Test + public void testImageResize() + { + String fileName = initialize("resized"); + String imagePath = GxImageUtil.resize(fileName, 300, 400, false); + + int imageHeight = GxImageUtil.getImageHeight(imagePath); + Assert.assertEquals(400, imageHeight); + + int imageWidth = GxImageUtil.getImageWidth(imagePath); + Assert.assertEquals(300, imageWidth); + } + + + @Test + public void testImageFlipHorizontally() + { + String fileName = initialize("flippedHorizontally"); + String imagePath = GxImageUtil.flipHorizontally(fileName); + + int imageHeight = GxImageUtil.getImageHeight(imagePath); + Assert.assertEquals(IMAGE_HEIGHT, imageHeight); + + int imageWidth = GxImageUtil.getImageWidth(imagePath); + Assert.assertEquals(IMAGE_WIDTH, imageWidth); + } + + @Test + public void testImageFlipVertically() + { + String fileName = initialize("flippedVertically"); + String imagePath = GxImageUtil.flipVertically(fileName); + + int imageHeight = GxImageUtil.getImageHeight(imagePath); + Assert.assertEquals(IMAGE_HEIGHT, imageHeight); + + int imageWidth = GxImageUtil.getImageWidth(imagePath); + Assert.assertEquals(IMAGE_WIDTH, imageWidth); + } + + @Test + public void testImageFileSize() + { + String fileName = initialize("imageSize"); + long fileSize = GxImageUtil.getFileSize(fileName); + Assert.assertEquals(113974, fileSize); + + } +} diff --git a/java/src/test/resources/bird-thumbnail.jpg b/java/src/test/resources/bird-thumbnail.jpg new file mode 100644 index 000000000..8e33f1bdd Binary files /dev/null and b/java/src/test/resources/bird-thumbnail.jpg differ