Optionally load image from data instead of filename.

This commit is contained in:
David Kocher
2026-05-11 20:40:07 +02:00
parent 1d735460a3
commit 2f183d4044
4 changed files with 25 additions and 59 deletions
@@ -20,6 +20,7 @@ package ch.cyberduck.core.resources;
import ch.cyberduck.binding.application.NSGraphics;
import ch.cyberduck.binding.application.NSImage;
import ch.cyberduck.binding.application.NSWorkspace;
import ch.cyberduck.binding.foundation.NSData;
import ch.cyberduck.core.Factory;
import ch.cyberduck.core.Local;
import ch.cyberduck.core.Path;
@@ -27,6 +28,7 @@ import ch.cyberduck.core.Permission;
import ch.cyberduck.core.local.Application;
import ch.cyberduck.core.preferences.PreferencesFactory;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.LogManager;
@@ -154,14 +156,20 @@ public class NSImageIconCache implements IconCache<NSImage> {
*/
@Override
public NSImage iconNamed(final String name, final Integer width, final Integer height) {
// Search for an object whose name was set explicitly using the setName: method and currently
// resides in the image cache
NSImage image = this.load(name, width);
if(null == name) {
return this.iconNamed("notfound.tiff", width, height);
}
NSImage image;
if(Base64.isBase64(name)) {
image = convert(name, NSImage.imageWithData(NSData.dataWithBase64EncodedString(name)), width, height);
}
else {
// Search for an object whose name was set explicitly using the setName: method and currently
// resides in the image cache
image = this.load(name, width);
}
if(null == image) {
if(null == name) {
return this.iconNamed("notfound.tiff", width, height);
}
else if(name.contains(PreferencesFactory.get().getProperty("local.delimiter"))) {
if(name.contains(PreferencesFactory.get().getProperty("local.delimiter"))) {
return cache(FilenameUtils.getName(name),
convert(FilenameUtils.getName(name), NSImage.imageWithContentsOfFile(name), width, height), width);
}
@@ -19,23 +19,17 @@ package ch.cyberduck.core;
* dkocher@cyberduck.ch
*/
import ch.cyberduck.core.exception.AccessDeniedException;
import ch.cyberduck.core.features.Location;
import ch.cyberduck.core.local.TemporaryFileServiceFactory;
import ch.cyberduck.core.preferences.PreferencesFactory;
import ch.cyberduck.core.serializer.Deserializer;
import ch.cyberduck.core.serializer.Serializer;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.text.StringSubstitutor;
import org.apache.commons.text.lookup.StringLookupFactory;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
@@ -135,14 +129,9 @@ public class Profile implements Protocol {
public static final String DEPRECATED_KEY = "Deprecated";
public static final String HELP_KEY = "Help";
private Local disk;
private Local icon;
public Profile(final Protocol parent, final Deserializer<?> dict) {
this.parent = parent;
this.dict = dict;
this.disk = this.write(this.value(DISK_KEY));
this.icon = this.write(this.value(ICON_KEY));
}
@Override
@@ -366,29 +355,20 @@ public class Profile implements Protocol {
@Override
public String disk() {
if(null == disk) {
final String v = this.value(DISK_KEY);
if(StringUtils.isBlank(v)) {
return parent.disk();
}
if(!disk.exists()) {
this.disk = this.write(this.value(DISK_KEY));
}
// Temporary file
return disk.getAbsolute();
return v;
}
@Override
public String icon() {
if(null == icon) {
if(null == disk) {
return parent.icon();
}
return this.disk();
final String v = this.value(ICON_KEY);
if(StringUtils.isBlank(v)) {
return parent.icon();
}
if(!icon.exists()) {
this.icon = this.write(this.value(ICON_KEY));
}
// Temporary file
return icon.getAbsolute();
return v;
}
@Override
@@ -396,30 +376,6 @@ public class Profile implements Protocol {
return parent.favicon();
}
/**
* Write temporary file with data
*
* @param icon Base64 encoded image information
* @return Path to file
*/
private Local write(final String icon) {
if(StringUtils.isBlank(icon)) {
return null;
}
final byte[] favicon = Base64.decodeBase64(icon);
final Local file = TemporaryFileServiceFactory.get().create(new AlphanumericRandomStringService().random());
try {
try (final OutputStream out = file.getOutputStream(false)) {
IOUtils.write(favicon, out);
}
return file;
}
catch(IOException | AccessDeniedException e) {
log.error("Error writing temporary file", e);
}
return null;
}
@Override
public boolean validate(final Credentials credentials, final LoginOptions options) {
return parent.validate(credentials, options);
@@ -222,6 +222,8 @@ public interface Protocol extends FeatureFactory, Comparable<Protocol>, Serializ
String getRegion();
/**
* Either a filename or Base64 encoded image data
*
* @return A mounted disk icon to display
*/
String disk();
@@ -33,7 +33,7 @@ public interface IconCache<I> {
}
/**
* @param name Icon filename with extension
* @param name Icon filename with extension or Base64 encoded image data
* @param size Requested size
* @return Cached image
*/