Log Message
Reworked to retain original image format. Provides more flexibility to user (can upload animated gifs), but puts more responsability on user to not exhaust heap memory.
Modified Paths
Diff
Modified: trunk/hudson/plugins/avatar/src/main/java/net/hurstfrost/jenkins/avatar/user/AvatarProperty.java (40930 => 40931)
--- trunk/hudson/plugins/avatar/src/main/java/net/hurstfrost/jenkins/avatar/user/AvatarProperty.java 2013-06-22 07:41:59 UTC (rev 40930)
+++ trunk/hudson/plugins/avatar/src/main/java/net/hurstfrost/jenkins/avatar/user/AvatarProperty.java 2013-06-23 08:13:56 UTC (rev 40931)
@@ -1,98 +1,128 @@
package net.hurstfrost.jenkins.avatar.user;
import hudson.Extension;
-import hudson.model.Action;
-import hudson.model.Hudson;
-import hudson.model.MyViewsProperty;
-import hudson.model.User;
-import hudson.model.UserProperty;
-import hudson.model.UserPropertyDescriptor;
+import hudson.model.*;
import hudson.model.Descriptor.FormException;
+import net.sf.json.JSONObject;
+import org.apache.commons.fileupload.FileItem;
+import org.apache.commons.io.IOUtils;
+import org.kohsuke.stapler.StaplerRequest;
+import org.kohsuke.stapler.StaplerResponse;
+import org.kohsuke.stapler.export.Exported;
-import java.awt.image.BufferedImage;
+import javax.imageio.ImageIO;
+import javax.imageio.ImageReader;
+import javax.imageio.stream.ImageInputStream;
+import javax.servlet.ServletException;
import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
import java.io.IOException;
+import java.util.Iterator;
import java.util.logging.Level;
import java.util.logging.Logger;
-import javax.imageio.ImageIO;
-import javax.servlet.ServletException;
-import javax.servlet.ServletOutputStream;
-
-import net.sf.json.JSONObject;
-
-import org.apache.commons.fileupload.FileItem;
-import org.kohsuke.stapler.StaplerRequest;
-import org.kohsuke.stapler.StaplerResponse;
-import org.kohsuke.stapler.export.Exported;
-
public class AvatarProperty extends UserProperty implements Action {
+ public static final int MAX_AVATAR_IMAGE_SIZE = 60 * 1000 * 1000;
private static final Logger log = Logger.getLogger(MyViewsProperty.class.getName());
private byte[] imageBytes;
- private transient byte[] replacementImageBytes;
+ private transient AvatarImage replacementImage;
+ private transient String lastError;
+ private transient String lastWarning;
- public byte[] getImageBytes() {
- return imageBytes;
- }
+ private AvatarImage avatarImage;
- public void setImageBytes(byte[] imageBytes) {
- this.imageBytes = imageBytes;
- }
+ private Object readResolve() {
+ if (imageBytes != null) {
+ // Upgrade from old version
+ try {
+ avatarImage = AvatarImage.fromBytes(imageBytes);
+ } catch (IOException e) {
+ log.warning("Couldn't interpret avatar image : " + e.getMessage());
+ }
+ imageBytes = null;
+ }
+
+ return this;
+ }
+
@Exported
public String getAvatarUrl() {
- if (imageBytes != null) {
- return getUnsavedAvatarUrl();
+ if (isHasAvatar()) {
+ return getAvatarImageUrl();
}
return null;
}
public String getUnsavedAvatarUrl() {
- return Hudson.getInstance().getRootUrl() + user.getUrl() + "/avatar/image";
+ return getAvatarImageUrl();
+ }
+
+ private String getAvatarImageUrl() {
+ return Hudson.getInstance().getRootUrl() + user.getUrl() + "/avatar/image";
+ }
+
+ public boolean isHasAvatar() {
+ return avatarImage != null;
}
-
- public boolean getHasAvatar() {
- return imageBytes != null && imageBytes.length > 0;
- }
- public boolean getHasAvatarBeforeSave() {
- if (replacementImageBytes == null) {
- return getHasAvatar();
- }
-
- return replacementImageBytes.length > 0;
+ public boolean isHasAvatarBeforeSave() {
+ if (replacementImage == null) {
+ return isHasAvatar();
+ }
+
+ return replacementImage.isValid();
}
public boolean getResetTrigger() {
- replacementImageBytes = null;
+ replacementImage = null;
return true;
}
+ public boolean isHasError() {
+ return lastError != null;
+ }
+
+ public String getLastError() {
+ String poppedError = lastError;
+
+ lastError = null;
+
+ return poppedError;
+ }
+
+ public boolean isHasWarning() {
+ return lastWarning != null;
+ }
+
+ public String getLastWarning() {
+ String poppedWarning = lastWarning;
+
+ lastWarning = null;
+
+ return poppedWarning;
+ }
+
public void doImage(StaplerRequest req, StaplerResponse rsp) throws IOException {
- byte[] imageToReturn = imageBytes;
+ AvatarImage imageToReturn = avatarImage;
if (req.getParameter("preview") != null) {
- if (replacementImageBytes != null) {
- imageToReturn = replacementImageBytes;
+ if (replacementImage != null) {
+ imageToReturn = replacementImage;
}
}
- if (imageToReturn == null || imageToReturn.length == 0) {
- log.log(Level.SEVERE, "No image set for user '" + user.getId() + "'");
+ if (imageToReturn == null) {
+ log.log(Level.WARNING, "No image set for user '" + user.getId() + "'");
return;
}
- rsp.setContentType("image/png");
- ServletOutputStream outputStream = rsp.getOutputStream();
- ImageIO.getWriterFormatNames();
+ rsp.setContentType(imageToReturn.mimeType);
try {
- BufferedImage image = ImageIO.read(new ByteArrayInputStream(imageToReturn));
- ImageIO.write(image, "png", outputStream);
+ IOUtils.write(imageToReturn.imageBytes, rsp.getOutputStream());
} catch (Exception e) {
log.log(Level.SEVERE, "Unable to write image for user '" + user.getId() + "'", e);
}
@@ -103,24 +133,25 @@
* File is placed in $JENKINS_HOME/userContent directory.
* @throws ServletException
*/
- public void doUpload(StaplerRequest req, StaplerResponse rsp) throws IOException, ServletException {
+ public void doUpload(StaplerRequest req, StaplerResponse rsp) throws IOException, ServletException, FormException {
FileItem file = req.getFileItem("avatarimage.file");
if (file != null && !file.getName().isEmpty()) {
- // Ensure we can interpret this file as an Image
- try {
- BufferedImage image = null;
- if ((image = ImageIO.read(file.getInputStream())) != null) {
- ByteArrayOutputStream bAOS = new ByteArrayOutputStream();
- ImageIO.write(image, "png", bAOS);
- replacementImageBytes = bAOS.toByteArray();
- }
- } catch (Exception e) {
- // Uploaded file is invalid, ignore
- }
+ if (file.getSize() > MAX_AVATAR_IMAGE_SIZE) {
+ lastError = "Uploaded image is too large.";
+ } else {
+ // Ensure we can interpret this file as an Image
+ try {
+ replacementImage = AvatarImage.fromBytes(file.get());
+ } catch (Exception e) {
+ // Uploaded file is invalid, ignore
+ lastError = e.getMessage();
+ }
+ }
} else {
// Indicate that image should be removed
- replacementImageBytes = new byte[0];
+ lastWarning = "Empty image uploaded. Avatar will be removed on save.";
+ replacementImage = new AvatarImage();
}
req.getView(this, "configIframe").forward(req, rsp);
@@ -130,14 +161,14 @@
public UserProperty reconfigure(StaplerRequest req, JSONObject form) throws FormException {
req.bindJSON(this, form);
- if (replacementImageBytes != null) {
- if (replacementImageBytes.length == 0) {
- imageBytes = null;
+ if (replacementImage != null) {
+ if (replacementImage.isValid()) {
+ avatarImage = replacementImage;
} else {
- imageBytes = replacementImageBytes;
+ avatarImage = null;
}
-
- replacementImageBytes = null;
+
+ replacementImage = null;
}
return this;
@@ -168,4 +199,37 @@
return new AvatarProperty();
}
}
+
+ public static class AvatarImage {
+ private byte[] imageBytes;
+ private String mimeType;
+ private String filenameSuffix;
+
+ static AvatarImage fromBytes(byte[] bytes) throws IOException {
+ ImageInputStream imageInputStream = ImageIO.createImageInputStream(new ByteArrayInputStream(bytes));
+ Iterator<ImageReader> imageReaders = ImageIO.getImageReaders(imageInputStream);
+ if (imageReaders.hasNext()) {
+ ImageReader imageReader = imageReaders.next();
+ String[] mimeTypes = imageReader.getOriginatingProvider().getMIMETypes();
+ String[] fileSuffixes = imageReader.getOriginatingProvider().getFileSuffixes();
+
+ if (mimeTypes.length > 0 && fileSuffixes.length > 0) {
+ AvatarImage avatarImage = new AvatarImage();
+ avatarImage.imageBytes = bytes;
+ avatarImage.mimeType = mimeTypes[0];
+ avatarImage.filenameSuffix = fileSuffixes[0];
+
+ log.log(Level.FINE, "Avatar image interpreted as " + avatarImage.mimeType + " ." + avatarImage.filenameSuffix);
+
+ return avatarImage;
+ }
+ }
+
+ return null;
+ }
+
+ public boolean isValid() {
+ return imageBytes != null;
+ }
+ }
}
Modified: trunk/hudson/plugins/avatar/src/main/resources/net/hurstfrost/jenkins/avatar/user/AvatarProperty/configIframe.jelly (40930 => 40931)
--- trunk/hudson/plugins/avatar/src/main/resources/net/hurstfrost/jenkins/avatar/user/AvatarProperty/configIframe.jelly 2013-06-22 07:41:59 UTC (rev 40930)
+++ trunk/hudson/plugins/avatar/src/main/resources/net/hurstfrost/jenkins/avatar/user/AvatarProperty/configIframe.jelly 2013-06-23 08:13:56 UTC (rev 40931)
@@ -9,27 +9,41 @@
<body>
<table style="border-collapse:collapse">
<tr>
- <td width="100px">
- <j:choose>
- <j:when test="${it.hasAvatarBeforeSave}">
- <img style="height:64px;background:url('/plugin/avatar/checker16x16.png') repeat" src=""
- </j:when>
- <j:otherwise>
- <span class="setting-name">No avatar</span>
- </j:otherwise>
- </j:choose>
+ <td width="100px" rowspan="2" style="padding-right: 6px">
+ <j:choose>
+ <j:when test="${it.hasAvatarBeforeSave}">
+ <img style="height:64px;background:url('/plugin/avatar/checker16x16.png') repeat" src=""
+ </j:when>
+ <j:otherwise>
+ <span class="setting-name">No avatar</span>
+ </j:otherwise>
+ </j:choose>
</td>
<td>
- <form method="post" action="" enctype="multipart/form-data">
- <span class="setting-name">Upload an avatar: </span>
- <input type="file" name="avatarimage.file" size="40"/>
- <st:nbsp/>
- <input type="submit" value="${%Upload}"/>
- <br/>
- <span class="setting-description">${%Images can be gif, jpg or png. Other types may not be supported.}</span>
- </form>
+ <form method="post" action="" enctype="multipart/form-data">
+ <span class="setting-name">Upload an avatar: </span>
+ <input type="file" name="avatarimage.file" size="40"/>
+ <st:nbsp/>
+ <input type="submit" value="${%Upload}"/>
+ <br/>
+ <span class="setting-description">${%Images can be gif, jpg or png. Other types may not be supported.}</span>
+ </form>
</td>
</tr>
+ <tr>
+ <td>
+ <j:choose>
+ <j:when test="${it.hasError}">
+ <p class="error">${it.lastError}</p>
+ </j:when>
+ </j:choose>
+ <j:choose>
+ <j:when test="${it.hasWarning}">
+ <p class="warning">${it.lastWarning}</p>
+ </j:when>
+ </j:choose>
+ </td>
+ </tr>
</table>
</body>
</html>
You received this message because you are subscribed to the Google Groups "Jenkins Commits" group.
To unsubscribe from this group and stop receiving emails from it, send an email to [email protected].
For more options, visit https://groups.google.com/groups/opt_out.
