Add AssetStorage and deprecate the old way of managing marker-images

This commit is contained in:
Lukas Rieger (Blue) 2022-12-02 15:42:54 +01:00
parent 3949ebcb56
commit a105baf81a
No known key found for this signature in database
GPG Key ID: 2D09EC5ED2687FF2
4 changed files with 195 additions and 23 deletions

View File

@ -0,0 +1,83 @@
package de.bluecolored.bluemap.api;
import de.bluecolored.bluemap.api.markers.POIMarker;
import de.bluecolored.bluemap.api.markers.HtmlMarker;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Optional;
/**
* A storage that is able to hold any "asset"-data for a map. For example images, icons, scripts or json-files.
*/
public interface AssetStorage {
/**
* Writes a new asset into this storage, overwriting any existent assets with the same name.<br>
* Use the returned {@link OutputStream} to write the asset-data. The asset will be added to the storage as soon as that stream
* gets closed!
* <p>
* Example:
* <pre>
* try (OutputStream out = assetStorage.writeAsset("image.png")) {
* ImageIO.write(image, "png", out);
* }
* </pre>
* </p>
* @param name The (unique) name for this asset
* @return An {@link OutputStream} that should be used to write the asset and closed once!
* @throws IOException when the underlying storage rises an IOException
*/
OutputStream writeAsset(String name) throws IOException;
/**
* Reads an asset from this storage.<br>
* Use the returned {@link InputStream} to read the asset-data.
* <p>
* Example:
* <pre>
* Optional&lt;InputStream&gt; optIn = assetStorage.readAsset("image.png");
* if (optIn.isPresent()) {
* try (InputStream in = optIn.get()) {
* BufferedImage image = ImageIO.read(in);
* }
* }
* </pre>
* </p>
* @param name The name of the asset that should be read from the storage.
* @return An {@link Optional} with an {@link InputStream} when the asset is found, from which the asset can be read.
* Or an empty optional if there is no asset with this name.
* @throws IOException when the underlying storage rises an IOException
*/
Optional<InputStream> readAsset(String name) throws IOException;
/**
* Checks if an asset exists in this storage without reading it.<br>
* This is useful if the asset has a lot of data and using {@link #readAsset(String)}
* just to check if the asset is present would be wasteful.
* @param name The name of the asset to check for
* @return <code>true</code> if the asset is found, <code>false</code> if not
* @throws IOException when the underlying storage rises an IOException
*/
boolean assetExists(String name) throws IOException;
/**
* Returns the relative URL that can be used by the webapp to request this asset.<br>
* This is the url that you can e.g. use in {@link POIMarker}s or {@link HtmlMarker}s to add an icon.<br>
* If there is no asset with this name, then this method returns the URL that an asset with such a name <i>would</i>
* have if it would be added later.
* @param name The name of the asset
* @return The relative URL for an asset with the given name
*/
String getAssetUrl(String name);
/**
* Deletes the asset with the given name from this storage, if it exists.<br>
* If there is no asset with this name, this method does nothing.
* @param name The name of the asset that should be deleted
* @throws IOException when the underlying storage rises an IOException
*/
void deleteAsset(String name) throws IOException;
}

View File

@ -60,6 +60,15 @@ public interface BlueMapMap {
@DebugDump
BlueMapWorld getWorld();
/**
* Getter for this map's {@link AssetStorage}. <br>
* Each map has its own storage for assets. Assets that are stored here will be available to every webapp that
* is displaying this map. E.g. these assets are also available in server-networks.
* @return the {@link AssetStorage} of this map
*/
@DebugDump
AssetStorage getAssetStorage();
/**
* Getter for a (modifiable) {@link Map} of {@link MarkerSet}s with the key being the {@link MarkerSet}'s id.
* Changing this map will change the {@link MarkerSet}s and markers displayed on the web-app for this map.

View File

@ -0,0 +1,97 @@
package de.bluecolored.bluemap.api;
import java.nio.file.Path;
import java.util.HashMap;
import java.util.Map;
/**
* This Content-Type registry is used by the internal webserver to get the content-type of an asset.<br>
* The most commonly used file-suffixes and their content-types are registered by default, but you can use the static
* methods of this class to add more, if you need them.<br>
* <b>Note:</b> that any additionally added types won't work if the user uses an external webserver to serve their map-files.
*/
public class ContentTypeRegistry {
private static final String DEFAULT_CONTENT_TYPE = "application/octet-stream";
private static final Map<String, String> SUFFIX_MAP = new HashMap<>();
static {
register("txt", "text/plain");
register("css", "text/css");
register("csv", "text/csv");
register("htm", "text/html");
register("html", "text/html");
register("js", "text/javascript");
register("xml", "text/xml");
register("png", "image/png");
register("jpg", "image/jpeg");
register("jpeg", "image/jpeg");
register("gif", "image/gif");
register("webp", "image/webp");
register("tif", "image/tiff");
register("tiff", "image/tiff");
register("svg", "image/svg+xml");
register("json", "application/json");
register("mp3", "audio/mpeg");
register("oga", "audio/ogg");
register("wav", "audio/wav");
register("weba", "audio/webm");
register("mp4", "video/mp4");
register("mpeg", "video/mpeg");
register("webm", "video/webm");
register("ttf", "font/ttf");
register("woff", "font/woff");
register("woff2", "font/woff2");
}
/**
* Derives the content-type (mime) string using a path denoting a file
* @param path The path pointing at the file
* @return The derived content-type string
*/
public static String fromPath(Path path) {
return fromFileName(path.getFileName().toString());
}
/**
* Derives the content-type (mime) string from the name of a file
* @param fileName The name of the file
* @return The derived content-type string
*/
public static String fromFileName(String fileName) {
int i = fileName.lastIndexOf('.');
if (i < 0) return DEFAULT_CONTENT_TYPE;
int s = fileName.lastIndexOf('/');
if (i < s) return DEFAULT_CONTENT_TYPE;
String suffix = fileName.substring(i + 1);
return fromFileSuffix(suffix);
}
/**
* Searches and returns the content-type for the provided file-suffix.
* @param suffix The type-suffix of a file-name
* @return The content-type string
*/
public static String fromFileSuffix(String suffix) {
String contentType = SUFFIX_MAP.get(suffix);
if (contentType != null) return contentType;
return DEFAULT_CONTENT_TYPE;
}
/**
* Registers a new file-suffix => content-type mapping to this registry.
* @param fileSuffix The type-suffix of a file-name
* @param contentType The content-type string
*/
public static void register(String fileSuffix, String contentType) {
SUFFIX_MAP.put(fileSuffix, contentType);
}
}

View File

@ -25,9 +25,6 @@
package de.bluecolored.bluemap.api;
import de.bluecolored.bluemap.api.debug.DebugDump;
import de.bluecolored.bluemap.api.markers.Marker;
import de.bluecolored.bluemap.api.markers.POIMarker;
import com.flowpowered.math.vector.Vector2i;
import java.awt.image.BufferedImage;
import java.io.IOException;
@ -59,31 +56,17 @@ public interface WebApp {
boolean getPlayerVisibility(UUID player);
/**
* Creates an image-file with the given {@link BufferedImage} somewhere in the web-root, so it can be used in the web-app (e.g. for {@link Marker}-icons).
*
* <p>The given <code>path</code> is used as file-name and (separated with '/') optional folders to organize the image-files.
* Do NOT include the file-ending! (e.g. <code>"someFolder/somePOIIcon"</code> will result in a file "somePOIIcon.png" in a folder "someFolder").</p>
* <p>If the image file with the given path already exists, it will be replaced.</p>
*
* @param image the image to create
* @param path the path/name of this image, the separator-char is '/'
* @return the relative address of the image in the web-app,
* which can be used as it is e.g. in the {@link POIMarker#setIcon(String, Vector2i)} method
* @throws IOException If an {@link IOException} is thrown while writing the image
* @deprecated You should use the {@link #getWebRoot()} method to create the image-files you need, or store map/marker
* specific images in the map's storage (See: {@link BlueMapMap#getAssetStorage()})!
*/
@Deprecated
String createImage(BufferedImage image, String path) throws IOException;
/**
* Lists all images that are available. This includes all images previously created with the {@link #createImage(BufferedImage, String)}
* function, but might include more.
* @return A map of available images where:
* <ul>
* <li>the <b>key</b> is the image path how it would be used in the "path" parameter of the {@link #createImage(BufferedImage, String)} method</li>
* <li>and the <b>value</b> is the relative address of the image. The same ones that are returned from the {@link #createImage(BufferedImage, String)} method</li>
* </ul>
* @throws IOException If an {@link IOException} is thrown while reading the images
* @deprecated You should use the {@link #getWebRoot()} method to find the image-files you need, or read map/marker
* specific images from the map's storage (See: {@link BlueMapMap#getAssetStorage()})!
*/
@DebugDump
@Deprecated
Map<String, String> availableImages() throws IOException;
}