I just want to be able to share a link to several images at once.
The most simple way would be transforming file listings of nginx. You can do that by making nginx output listings as XML and then transform them using XSLT. Built-in module ngx_http_autoindex_module
will do the former and usually dynamic module ngx_http_xslt_filter_module
(aka ngx_http_xslt_module
) will do the latter.
First, load the module in nginx.conf
if needed:
load_module "/usr/lib/nginx/modules/ngx_http_xslt_filter_module.so";
Then, in your sites-available/website.com
, add a location that tells nginx to transform the xml index using stlylesheet gal.xslt
and pass a the name of the folder as a parameter.
location ~ /gal/([A-z]+)/$ {
autoindex on;
autoindex_format xml;
xslt_string_param title $1;
xslt_stylesheet gal.xslt;
try_files $uri/ =404;
}
Finally, create gal.xslt
in /etc/nginx
that says,
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html" encoding="utf-8" indent="yes" />
<xsl:template match="/">
<xsl:text disable-output-escaping='yes'><!DOCTYPE html></xsl:text>
<html>
<head>
<title><xsl:value-of select="$title" /></title>
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<style>
img, video {
display: block;
max-width: 20cm;
max-height: 20cm;
margin: 2mm;
vertical-align: bottom;
image-orientation: from-image;
}
@media all and (max-width: 20.4cm) {
img {
max-width: calc(100% - 4mm);
}
}
body {
margin: 0;
}
</style>
</head>
<body>
<xsl:for-each select="list/file">
<xsl:choose>
<xsl:when test="contains(' mp4 webm mkv avi wmv flv ogv ', concat(' ', substring-after(., '.'), ' '))">
<video controls="" src="{.}" alt="{.}" title="{.}"/>
</xsl:when>
<xsl:otherwise>
<img src="{.}" alt="{.}" title="{.}"/>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
Now put some images into /var/www/html/gal/foo
, restart nginx, navigate to website.com/gal/foo
and you will see a simple but usable and responsive image gallery.
Thanks @squirrel that's really simply yet powerful.
I've tweaked the xslt - this version gal.xslt below:
Each image is click-able, so you can see the full size image.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html" encoding="utf-8" indent="yes" />
<xsl:template match="/">
<xsl:text disable-output-escaping='yes'><!DOCTYPE html></xsl:text>
<html>
<head>
<title><xsl:value-of select="$title" /></title>
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<style>
img {
display: inline;
width: 23%;
margin: 2mm;
vertical-align: bottom;
}
@media all and (max-width: 20.4cm) {
img {
max-width: calc(100% - 4mm);
}
}
body {
margin: 0;
}
</style>
</head>
<body>
<xsl:for-each select="list/file">
<a href="{.}" title="click to enlarge">
<img src="{.}" alt="{.}"/>
</a>
</xsl:for-each>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
Cheers,
Greg
I tweaked a bit more: fancybox, thumbnail handling (with pregenerated thumbnails in the thumbs directory), ability to download all images as a browser generated zip file.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html" encoding="utf-8" indent="yes" />
<xsl:template match="/">
<xsl:text disable-output-escaping='yes'><!DOCTYPE html></xsl:text>
<html>
<head>
<title><xsl:value-of select="$title" /></title>
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fancyapps/[email protected]/dist/jquery.fancybox.min.css" integrity="sha256-Vzbj7sDDS/woiFS3uNKo8eIuni59rjyNGtXfstRzStA=" crossorigin="anonymous"/>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/jquery.min.js" integrity="sha256-9/aliU8dGd2tb6OSsuzixeV4y/faTqgFtohetphbbj0=" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/@fancyapps/[email protected]/dist/jquery.fancybox.min.js" integrity="sha256-yt2kYMy0w8AbtF89WXb2P1rfjcP/HTHLT7097U8Y5b8=" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/jszip.min.js" integrity="sha256-PZ/OvdXxEW1u3nuTAUCSjd4lyaoJ3UJpv/X11x2Gi5c=" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/FileSaver.min.js" integrity="sha256-xoh0y6ov0WULfXcLMoaA6nZfszdgI8w2CEJ/3k8NBIE=" crossorigin="anonymous"></script>
<style>img { display: block; }</style>
</head>
<body>
<h1 style="text-align: center;"><xsl:value-of select="$title"/></h1>
<div style="border-bottom: 1px solid gray; margin-bottom: 1rem;"></div>
<div style="display: flex; flex-wrap: wrap; gap: 2px; justify-content: center;">
<xsl:for-each select="list/file">
<a href="{.}" data-fancybox="gallery">
<xsl:choose>
<xsl:when test="count(/list/directory[text() = 'thumbs'])">
<img loading="lazy" src="thumbs/{.}"/>
</xsl:when>
<xsl:otherwise>
<img loading="lazy" src="{.}" height="200"/>
</xsl:otherwise>
</xsl:choose>
</a>
</xsl:for-each>
</div>
<script>
async function downloadAll() {
const zip = JSZip();
const folder = zip.folder('<xsl:value-of select="$title" />');
const files = [
<xsl:for-each select="list/file">
'<xsl:value-of select="." />',
</xsl:for-each>
];
for(const i in files) {
const file = files[i];
const resp = await fetch(file);
folder.file(file, resp.blob());
$.fancybox.animate($.fancybox.getInstance().SlideShow.$progress.show(),{scaleX: i/files.length}, 0.1);
}
const zipFile = await zip.generateAsync({type: 'blob'});
saveAs(zipFile, '<xsl:value-of select="$title" />' + '.zip');
$.fancybox.animate($.fancybox.getInstance().SlideShow.$progress.show(),{scaleX: 0}, 0.1);
}
$('[data-fancybox="gallery"]').fancybox({
buttons: [
"zoom",
"slideShow",
"fullScreen",
"download",
"downloadAll",
"close"
],
btnTpl: {
downloadAll:
'<a class="fancybox-button fancybox-button--download" title="Download All" href="javascript:downloadAll()">' +
'<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 10v6m0 0l-3-3m3 3l3-3M3 17V7a2 2 0 012-2h6l2 2h6a2 2 0 012 2v8a2 2 0 01-2 2H5a2 2 0 01-2-2z" style="fill:unset; stroke-width:2"/></svg>' +
'</a>',
},
});
$('[data-fancybox="gallery"]')[0].click();
</script>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
I tweaked also a bit more :-) and prepare generated configuration with the JS and CS. Main features are:
You can check it on https://github.com/forrest79/StaticNginxGallery.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With