Skip to content
GitLab
Menu
Projects
Groups
Snippets
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in
Toggle navigation
Menu
Open sidebar
Eclipse Foundation
IT
Websites
membership.eclipse.org
Commits
efbd10b7
Commit
efbd10b7
authored
Dec 16, 2021
by
Martin Lowe
🇨🇦
Browse files
#410
- Add image resizing, defaulting to a max dimension of 200px
parent
24f40174
Changes
3
Hide whitespace changes
Inline
Side-by-side
pom.xml
View file @
efbd10b7
...
...
@@ -134,6 +134,13 @@
<version>
${openhtml.version}
</version>
</dependency>
<!-- Image rescaling - better results than vanilla -->
<dependency>
<groupId>
org.imgscalr
</groupId>
<artifactId>
imgscalr-lib
</artifactId>
<version>
4.2
</version>
</dependency>
<!-- Annotation preprocessors - reduce all of the boiler plate -->
<dependency>
<groupId>
com.google.auto.value
</groupId>
...
...
@@ -261,7 +268,7 @@
<artifactId>
maven-surefire-plugin
</artifactId>
<version>
${surefire-plugin.version}
</version>
<configuration>
<skipTests>
false
</skipTests>
<skipTests>
false
</skipTests>
<systemPropertyVariables>
<java.util.logging.manager>
org.jboss.logmanager.LogManager
</java.util.logging.manager>
<maven.home>
${maven.home}
</maven.home>
...
...
src/main/java/org/eclipsefoundation/react/service/ImageStoreService.java
View file @
efbd10b7
...
...
@@ -73,7 +73,7 @@ public interface ImageStoreService {
String
webRoot
();
@WithDefault
(
"
65
000"
)
@WithDefault
(
"
1000
000"
)
long
maxSizeInBytes
();
@WithDefault
(
""
)
...
...
@@ -94,6 +94,9 @@ public interface ImageStoreService {
@WithDefault
(
"true"
)
boolean
enabled
();
@WithDefault
(
"200"
)
int
maxDimension
();
@WithDefault
(
"0.80f"
)
@Max
(
1
)
float
factor
();
...
...
src/main/java/org/eclipsefoundation/react/service/impl/DefaultImageStoreService.java
View file @
efbd10b7
...
...
@@ -52,6 +52,7 @@ import org.eclipsefoundation.persistence.service.FilterService;
import
org.eclipsefoundation.react.helper.ImageFileHelper
;
import
org.eclipsefoundation.react.namespace.ImageStoreFormat
;
import
org.eclipsefoundation.react.service.ImageStoreService
;
import
org.imgscalr.Scalr
;
import
org.jboss.resteasy.specimpl.MultivaluedMapImpl
;
import
org.slf4j.Logger
;
import
org.slf4j.LoggerFactory
;
...
...
@@ -145,44 +146,24 @@ public class DefaultImageStoreService implements ImageStoreService {
Path
p
=
imageStoreRoot
.
resolve
(
ImageFileHelper
.
getFileNameWithExtension
(
fileName
,
format
,
mimeType
));
BasicFileAttributeView
attrView
=
Files
.
getFileAttributeView
(
p
,
BasicFileAttributeView
.
class
);
try
{
// for images bigger than the current max that don't already exist, refuse to write them
byte
[]
bytes
=
imageBytes
.
get
();
if
(
bytes
==
null
)
{
LOGGER
.
warn
(
"Could not generate image file with name {} and format {}, no image passed"
,
fileName
,
format
);
// cannot provide image, return null
return
null
;
}
else
if
(
bytes
.
length
>
config
.
maxSizeInBytes
()
&&
(!
Files
.
exists
(
p
)
||
!
approximatelyMatch
((
long
)
bytes
.
length
,
attrView
.
readAttributes
().
size
(),
1000
)))
{
}
// compress image and then compare max size
byte
[]
compressedImage
=
compress
(
bytes
,
mimeType
);
if
(
compressedImage
.
length
>
config
.
maxSizeInBytes
()
&&
(!
Files
.
exists
(
p
)
||
!
approximatelyMatch
((
long
)
compressedImage
.
length
,
attrView
.
readAttributes
().
size
(),
1000
)))
{
throw
new
BadRequestException
(
"Passed image is larger than allowed size of '"
+
config
.
maxSizeInBytes
()
+
"' bytes"
);
}
// if enabled, update the EclipseDB on logo update when image name is numeric
// max size check is related to max blob size of 64kb
// TODO remove once the Drupal API references this API as this will no longer be needed then
if
(
config
.
persistToDb
()
&&
format
.
isPresent
()
&&
ImageStoreFormat
.
ImageStoreFormats
.
WEB
.
equals
(
format
.
get
())
&&
StringUtils
.
isNumeric
(
fileName
))
{
// get a ref to the given organization information object
// get ref and update the object
MultivaluedMap
<
String
,
String
>
params
=
new
MultivaluedMapImpl
<>();
params
.
add
(
DefaultUrlParameterNames
.
ID
.
getName
(),
fileName
);
List
<
OrganizationInformation
>
infoRefs
=
dao
.
get
(
new
RDBMSQuery
<>(
new
RequestWrapper
(),
filters
.
get
(
OrganizationInformation
.
class
),
params
));
// if ref doesn't exist, create one
OrganizationInformation
oi
;
if
(
infoRefs
.
isEmpty
())
{
oi
=
new
OrganizationInformation
();
oi
.
setOrganizationID
(
Integer
.
valueOf
(
fileName
));
oi
.
setCompanyUrl
(
""
);
}
else
{
oi
=
infoRefs
.
get
(
0
);
}
oi
.
setLargeLogo
(
bytes
);
oi
.
setSmallLogo
(
bytes
);
oi
.
setLargeMime
(
mimeType
);
oi
.
setSmallMime
(
mimeType
);
dao
.
add
(
new
RDBMSQuery
<>(
new
RequestWrapper
(),
filters
.
get
(
OrganizationInformation
.
class
)),
Arrays
.
asList
(
oi
));
}
handleDBPersist
(
fileName
,
compressedImage
,
mimeType
,
format
);
// write will create and overwrite file by default if it exists
return
getWebUrl
(
Files
.
write
(
p
,
compress
(
bytes
,
mimeType
)));
...
...
@@ -228,6 +209,49 @@ public class DefaultImageStoreService implements ImageStoreService {
});
}
/**
* Handles persistence to the DB layer for images. Includes checks for size, format, and format before persisting to
* the database.
*
* @param fileName name of the file to be persisted
* @param compressedImage the compressed image bytes
* @param mimeType the mime type of the image
* @param format the format of the file
*/
private
void
handleDBPersist
(
String
fileName
,
byte
[]
compressedImage
,
String
mimeType
,
Optional
<
ImageStoreFormat
>
format
)
{
if
(
config
.
persistToDb
()
&&
format
.
isPresent
()
&&
ImageStoreFormat
.
ImageStoreFormats
.
WEB
.
equals
(
format
.
get
())
&&
StringUtils
.
isNumeric
(
fileName
))
{
if
(
compressedImage
.
length
<
65535
)
{
LOGGER
.
warn
(
"Cannot persist image with name {}, size of compressed image is over max size of table"
,
fileName
);
}
else
{
// get a ref to the given organization information object
MultivaluedMap
<
String
,
String
>
params
=
new
MultivaluedMapImpl
<>();
params
.
add
(
DefaultUrlParameterNames
.
ID
.
getName
(),
fileName
);
List
<
OrganizationInformation
>
infoRefs
=
dao
.
get
(
new
RDBMSQuery
<>(
new
RequestWrapper
(),
filters
.
get
(
OrganizationInformation
.
class
),
params
));
// if ref doesn't exist, create one
OrganizationInformation
oi
;
if
(
infoRefs
.
isEmpty
())
{
oi
=
new
OrganizationInformation
();
oi
.
setOrganizationID
(
Integer
.
valueOf
(
fileName
));
oi
.
setCompanyUrl
(
""
);
}
else
{
oi
=
infoRefs
.
get
(
0
);
}
// as long as this org exists, update the logo
oi
.
setLargeLogo
(
compressedImage
);
oi
.
setSmallLogo
(
compressedImage
);
oi
.
setLargeMime
(
mimeType
);
oi
.
setSmallMime
(
mimeType
);
dao
.
add
(
new
RDBMSQuery
<>(
new
RequestWrapper
(),
filters
.
get
(
OrganizationInformation
.
class
)),
Arrays
.
asList
(
oi
));
}
}
}
/**
* Fuzzy match used to check byte size of files. Used just in case there is some minor changes to the data done via
* metadata or the like.
...
...
@@ -270,7 +294,7 @@ public class DefaultImageStoreService implements ImageStoreService {
ImageOutputStream
ios
=
ImageIO
.
createImageOutputStream
(
os
))
{
// read in the image bytes for image io
// Note: if this fails due to an zlib exception, the image is most likely corrupt somehow
BufferedImage
image
=
ImageIO
.
read
(
new
ByteArrayInputStream
(
imageBytes
));
BufferedImage
image
=
rescale
(
ImageIO
.
read
(
new
ByteArrayInputStream
(
imageBytes
))
)
;
// get the image writer for the current mime type
Iterator
<
ImageWriter
>
writers
=
ImageIO
...
...
@@ -298,4 +322,21 @@ public class DefaultImageStoreService implements ImageStoreService {
}
}
}
/**
* Using Scalr library, uses quality-focused resizing algorithms to shrink images larger than the configured maximum
* dimension.
*
* @param original the original image to resize
* @return the resized image if larger than max dimensions, or original image
*/
private
BufferedImage
rescale
(
BufferedImage
original
)
{
// check if we need to rescale the image to save cycles
if
(
original
.
getHeight
()
<
config
.
compression
().
maxDimension
()
&&
original
.
getWidth
()
<
config
.
compression
().
maxDimension
())
{
return
original
;
}
// use Scalr to maintain ratio and resize image
return
Scalr
.
resize
(
original
,
Scalr
.
Method
.
QUALITY
,
config
.
compression
().
maxDimension
());
}
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment