Skip to content

Commit 905074c

Browse files
authored
Image API External Storage Support (#525)
* Support for ImageHeight and Width when Storage is Enabled * Illigal Argument Exception catch * Support GetFileSize * Crop, Resize, Scale, Rotate support for S3. * Remove unused code * Crop, Resize, Scale, Rotate support for S3. * Add UnitTests * GXFile should detect if it is already a absolute file path. * Image API support Local * Fix Code Quality * Not working in some scenarios for external storage urls * GxFile more defensive code
1 parent 96a72c0 commit 905074c

File tree

12 files changed

+312
-71
lines changed

12 files changed

+312
-71
lines changed

common/src/main/java/com/genexus/util/GXExternalFileInfo.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import com.genexus.CommonUtil;
99
import com.genexus.db.driver.*;
1010
import com.genexus.common.interfaces.SpecificImplementation;
11+
import com.genexus.db.driver.ResourceAccessControlList;
1112

1213
import java.util.Date;
1314

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package com.genexus.util;
2+
3+
public enum GxFileInfoSourceType {
4+
Unknown,
5+
LocalFile,
6+
ExternalFile
7+
}

gxoffice/src/main/java/com/genexus/gxoffice/poi/sxssf/ExcelDocument.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package com.genexus.gxoffice.poi.sxssf;
22

3+
import com.genexus.util.GxFileInfoSourceType;
34
import org.apache.poi.xssf.streaming.SXSSFWorkbook;
45
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
56

@@ -28,8 +29,7 @@ public short Open(String fileName) {
2829
return errCod;
2930
}
3031
} else {
31-
boolean isAbsolute = new java.io.File(fileName).isAbsolute();
32-
GXFile file = new GXFile(fileName, Constants.EXTERNAL_UPLOAD_ACL, isAbsolute);
32+
GXFile file = new GXFile("", fileName, Constants.EXTERNAL_UPLOAD_ACL, GxFileInfoSourceType.Unknown);
3333
if (file.exists()) {
3434
// System.out.println("Opening..");
3535
workBook = new SXSSFWorkbook(new XSSFWorkbook(file.getStream()));

gxoffice/src/main/java/com/genexus/gxoffice/poi/xssf/ExcelDocument.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import java.io.ByteArrayInputStream;
44
import java.io.ByteArrayOutputStream;
55

6+
import com.genexus.util.GxFileInfoSourceType;
67
import org.apache.poi.ss.usermodel.Row;
78
import org.apache.poi.ss.usermodel.Sheet;
89
import org.apache.poi.ss.usermodel.Workbook;
@@ -36,8 +37,7 @@ public short Open(String fileName) {
3637
return errCod;
3738
}
3839
} else {
39-
boolean isAbsolute = new java.io.File(fileName).isAbsolute();
40-
GXFile file = new GXFile(fileName, Constants.EXTERNAL_UPLOAD_ACL, isAbsolute);
40+
GXFile file = new GXFile("", fileName, Constants.EXTERNAL_UPLOAD_ACL, GxFileInfoSourceType.Unknown);
4141
if (file.exists()) {
4242
// System.out.println("Opening..");
4343
workBook = new XSSFWorkbook(file.getStream());

java/src/main/java/com/genexus/GxImageUtil.java

Lines changed: 99 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -5,99 +5,146 @@
55
import java.awt.geom.AffineTransform;
66
import java.awt.image.AffineTransformOp;
77
import java.awt.image.BufferedImage;
8-
import java.io.File;
9-
import java.io.FileOutputStream;
10-
import java.io.IOException;
8+
import java.io.*;
119

10+
import com.genexus.db.driver.ResourceAccessControlList;
11+
import com.genexus.util.GxFileInfoSourceType;
1212
import com.genexus.util.GXFile;
1313
import org.apache.logging.log4j.Logger;
1414

1515
public class GxImageUtil {
1616
private static Logger log = org.apache.logging.log4j.LogManager.getLogger(GxImageUtil.class);
17+
private static int INVALID_CODE = -1;
1718

18-
private static String getImageAbsolutePath(String imageFile){
19-
if (CommonUtil.isUploadPrefix(imageFile)) {
20-
return new GXFile(imageFile).getAbsolutePath();
19+
private static InputStream getInputStream(String filePathOrUrl) throws IOException {
20+
return getGXFile(filePathOrUrl).getStream();
21+
}
22+
23+
private static BufferedImage createBufferedImageFromURI(String filePathOrUrl) throws IOException
24+
{
25+
try (InputStream is = getGXFile(filePathOrUrl).getStream()) {
26+
return ImageIO.read(is);
27+
}
28+
catch (IOException e) {
29+
log.error("Failed to read image stream: " + filePathOrUrl);
30+
throw e;
2131
}
22-
String defaultPath = com.genexus.ModelContext.getModelContext().getHttpContext().getDefaultPath();
23-
return imageFile.startsWith(defaultPath)? imageFile : defaultPath + imageFile.replace("/", File.separator);
32+
}
33+
34+
private static GXFile getGXFile(String filePathOrUrl) {
35+
String basePath = (com.genexus.ModelContext.getModelContext() != null) ? com.genexus.ModelContext.getModelContext().getHttpContext().getDefaultPath(): "";
36+
return new GXFile(basePath, filePathOrUrl, ResourceAccessControlList.Default, GxFileInfoSourceType.Unknown);
2437
}
2538

2639
public static long getFileSize(String imageFile){
27-
return new File(getImageAbsolutePath(imageFile)).length();
40+
if (!isValidInput(imageFile))
41+
return INVALID_CODE;
42+
43+
return new GXFile(imageFile).getLength();
2844
}
2945

3046
public static int getImageHeight(String imageFile) {
47+
if (!isValidInput(imageFile))
48+
return INVALID_CODE;
49+
3150
try {
32-
BufferedImage image = ImageIO.read(new File(getImageAbsolutePath(imageFile)));
33-
return image.getHeight();
51+
return createBufferedImageFromURI(imageFile).getHeight();
3452
}
35-
catch (IOException e) {
53+
catch (Exception e) {
3654
log.error("getImageHeight " + imageFile + " failed" , e);
37-
return 0;
3855
}
56+
return INVALID_CODE;
57+
}
58+
59+
private static boolean isValidInput(String imageFile) {
60+
boolean isValid = imageFile != null && imageFile.length() > 0;
61+
if (!isValid) {
62+
log.debug("Image Api - FileName cannot be empty");
63+
}
64+
return isValid;
3965
}
4066

4167
public static int getImageWidth(String imageFile) {
68+
if (!isValidInput(imageFile))
69+
return INVALID_CODE;
70+
4271
try {
43-
BufferedImage image = ImageIO.read(new File(getImageAbsolutePath(imageFile)));
44-
return image.getWidth();
72+
return createBufferedImageFromURI(imageFile).getWidth();
4573
}
46-
catch (IOException e) {
74+
catch (Exception e) {
4775
log.error("getImageWidth " + imageFile + " failed" , e);
48-
return 0;
4976
}
77+
return INVALID_CODE;
5078
}
5179

52-
public static String crop(String imageFile, int x, int y, int width, int height){
80+
public static String crop(String imageFile, int x, int y, int width, int height) {
81+
if (!isValidInput(imageFile))
82+
return "";
83+
5384
try {
54-
String absolutePath = getImageAbsolutePath(imageFile);
55-
BufferedImage image = ImageIO.read(new File(absolutePath));
56-
BufferedImage cropedImage = image.getSubimage(x, y, width, height);
57-
ImageIO.write(cropedImage, CommonUtil.getFileType(absolutePath), new FileOutputStream(absolutePath));
85+
BufferedImage image = createBufferedImageFromURI(imageFile);
86+
BufferedImage croppedImage = image.getSubimage(x, y, width, height);
87+
writeImage(croppedImage, imageFile);
5888
}
59-
catch (IOException e) {
89+
catch (Exception e) {
6090
log.error("crop " + imageFile + " failed" , e);
6191
}
6292
return imageFile;
6393
}
6494

65-
public static String flipHorizontally(String imageFile){
95+
private static void writeImage(BufferedImage croppedImage, String destinationFilePathOrUrl) throws IOException {
96+
try (ByteArrayOutputStream outStream = new ByteArrayOutputStream()) {
97+
ImageIO.write(croppedImage, CommonUtil.getFileType(destinationFilePathOrUrl), outStream);
98+
try (ByteArrayInputStream inStream = new ByteArrayInputStream(outStream.toByteArray())) {
99+
GXFile file = getGXFile(destinationFilePathOrUrl);
100+
file.create(inStream, true);
101+
file.close();
102+
}
103+
}
104+
}
105+
106+
public static String flipHorizontally(String imageFile) {
107+
if (!isValidInput(imageFile))
108+
return "";
109+
66110
try {
67-
String absolutePath = getImageAbsolutePath(imageFile);
68-
BufferedImage image = ImageIO.read(new File(absolutePath));
111+
BufferedImage image = createBufferedImageFromURI(imageFile);
69112
AffineTransform tx = AffineTransform.getScaleInstance(-1, 1);
70113
tx.translate(-image.getWidth(null), 0);
71114
AffineTransformOp op = new AffineTransformOp(tx, AffineTransformOp.TYPE_NEAREST_NEIGHBOR);
72-
BufferedImage flipedImage = op.filter(image, null);
73-
ImageIO.write(flipedImage, CommonUtil.getFileType(absolutePath), new FileOutputStream(absolutePath));
115+
BufferedImage flippedImage = op.filter(image, null);
116+
writeImage(flippedImage, imageFile);
74117
}
75-
catch (IOException e) {
118+
catch (Exception e) {
76119
log.error("flip horizontal " + imageFile + " failed" , e);
77120
}
78121
return imageFile;
79122
}
80123

81-
public static String flipVertically(String imageFile){
124+
public static String flipVertically(String imageFile) {
125+
if (!isValidInput(imageFile))
126+
return "";
127+
82128
try {
83-
String absolutePath = getImageAbsolutePath(imageFile);
84-
BufferedImage image = ImageIO.read(new File(absolutePath));
129+
BufferedImage image = createBufferedImageFromURI(imageFile);
85130
AffineTransform tx = AffineTransform.getScaleInstance(1, -1);
86131
tx.translate(0, -image.getHeight(null));
87132
AffineTransformOp op = new AffineTransformOp(tx, AffineTransformOp.TYPE_NEAREST_NEIGHBOR);
88-
BufferedImage flipedImage = op.filter(image, null);
89-
ImageIO.write(flipedImage, CommonUtil.getFileType(absolutePath), new FileOutputStream(absolutePath));
133+
BufferedImage flippedImage = op.filter(image, null);
134+
writeImage(flippedImage, imageFile);
90135
}
91-
catch (IOException e) {
136+
catch (Exception e) {
92137
log.error("flip vertical " + imageFile + " failed" , e);
93138
}
94139
return imageFile;
95140
}
96141

97-
public static String resize(String imageFile, int width, int height, boolean keepAspectRatio){
142+
public static String resize(String imageFile, int width, int height, boolean keepAspectRatio) {
143+
if (!isValidInput(imageFile))
144+
return "";
145+
98146
try {
99-
String absolutePath = getImageAbsolutePath(imageFile);
100-
BufferedImage image = ImageIO.read(new File(absolutePath));
147+
BufferedImage image = createBufferedImageFromURI(imageFile);
101148
if (keepAspectRatio) {
102149
double imageHeight = image.getHeight();
103150
double imageWidth = image.getWidth();
@@ -112,34 +159,37 @@ public static String resize(String imageFile, int width, int height, boolean kee
112159
Graphics2D g2d = resizedImage.createGraphics();
113160
g2d.drawImage(image, 0, 0, width, height, null);
114161
g2d.dispose();
115-
ImageIO.write(resizedImage, CommonUtil.getFileType(absolutePath), new FileOutputStream(absolutePath));
162+
writeImage(resizedImage, imageFile);
116163
}
117-
catch (IOException e) {
164+
catch (Exception e) {
118165
log.error("resize " + imageFile + " failed" , e);
119166
}
120167
return imageFile;
121168
}
122169

123-
public static String scale(String imageFile, short percent){
170+
public static String scale(String imageFile, short percent) {
171+
if (!isValidInput(imageFile))
172+
return "";
173+
124174
try {
125-
String absolutePath = getImageAbsolutePath(imageFile);
126-
BufferedImage image = ImageIO.read(new File(absolutePath));
175+
BufferedImage image = createBufferedImageFromURI(imageFile);
127176
imageFile = resize(imageFile, image.getWidth() * percent / 100, image.getHeight() * percent / 100,true);
128177
}
129-
catch (IOException e) {
178+
catch (Exception e) {
130179
log.error("scale " + imageFile + " failed" , e);
131180
}
132181
return imageFile;
133182
}
134183

135-
public static String rotate(String imageFile, short angle){
184+
public static String rotate(String imageFile, short angle) {
185+
if (!isValidInput(imageFile))
186+
return "";
136187
try {
137-
String absolutePath = getImageAbsolutePath(imageFile);
138-
BufferedImage image = ImageIO.read(new File(absolutePath));
188+
BufferedImage image = createBufferedImageFromURI(imageFile);
139189
BufferedImage rotatedImage = rotateImage(image, angle);
140-
ImageIO.write(rotatedImage, CommonUtil.getFileType(absolutePath), new FileOutputStream(absolutePath));
190+
writeImage(rotatedImage, imageFile);
141191
}
142-
catch (IOException e) {
192+
catch (Exception e) {
143193
log.error("rotate " + imageFile + " failed" , e);
144194
}
145195
return imageFile;

java/src/main/java/com/genexus/PrivateUtilities.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919
import java.lang.reflect.Field;
2020
import java.net.HttpURLConnection;
2121
import java.net.URL;
22+
import java.nio.file.InvalidPathException;
23+
import java.nio.file.Paths;
2224
import java.util.Properties;
2325
import java.util.Random;
2426
import java.util.zip.DeflaterOutputStream;
@@ -609,6 +611,20 @@ private static File parent(File f) {
609611
return new File(dirname);
610612
}
611613

614+
/**
615+
* <pre>
616+
* Checks if a string is an absolute path to local file system.
617+
* Null safe.
618+
* </pre>
619+
*/
620+
public static boolean isAbsoluteFilePath(String path) {
621+
try {
622+
return Paths.get(path).isAbsolute();
623+
} catch (InvalidPathException | NullPointerException ex) {
624+
return false;
625+
}
626+
}
627+
612628
public static String addLastPathSeparator(String dir)
613629
{
614630
return addLastChar(dir, File.separator);

java/src/main/java/com/genexus/db/driver/GXResultSet.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
import com.genexus.internet.HttpContext;
3030
import com.genexus.util.GXFile;
3131
import com.genexus.util.GXServices;
32+
import com.genexus.db.driver.ResourceAccessControlList;
3233

3334
public final class GXResultSet implements ResultSet, com.genexus.db.IFieldGetter, IGXResultSet
3435
{

java/src/main/java/com/genexus/specific/java/FileUtils.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
package com.genexus.specific.java;
22

33
import com.genexus.common.classes.AbstractGXFile;
4-
import com.genexus.util.GXFile;
54
import com.genexus.db.driver.ResourceAccessControlList;
5+
import com.genexus.util.GXFile;
6+
import com.genexus.util.GxFileInfoSourceType;
67

78
import java.io.File;
89
import java.io.IOException;
@@ -47,7 +48,7 @@ public void copyFile(File file, File file2) throws IOException {
4748

4849
@Override
4950
public AbstractGXFile createFile(String file, ResourceAccessControlList acl, boolean local) {
50-
return new GXFile(file, acl, local);
51+
return new GXFile(file, acl, local ? GxFileInfoSourceType.LocalFile: GxFileInfoSourceType.Unknown);
5152
}
5253

5354
}

0 commit comments

Comments
 (0)