feat:Support compress image view
This commit is contained in:
parent
b8300b7121
commit
8af40d28a7
@ -1,5 +1,6 @@
|
||||
<template>
|
||||
<div class="item"
|
||||
<div
|
||||
class="item"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
draggable="true"
|
||||
@ -11,9 +12,12 @@
|
||||
@touchstart="touchstart"
|
||||
:data-dir="isDir"
|
||||
:aria-label="name"
|
||||
:aria-selected="isSelected">
|
||||
:aria-selected="isSelected"
|
||||
>
|
||||
<div>
|
||||
<i class="material-icons">{{ icon }}</i>
|
||||
<img v-if="type==='image'" :src="compressUrl" />
|
||||
<i v-else class="material-icons">{{ icon }}</i>
|
||||
<!-- <i class="material-icons">{{ icon }}</i> -->
|
||||
</div>
|
||||
|
||||
<div>
|
||||
@ -30,140 +34,146 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapMutations, mapGetters, mapState } from 'vuex'
|
||||
import filesize from 'filesize'
|
||||
import moment from 'moment'
|
||||
import { files as api } from '@/api'
|
||||
import { mapMutations, mapGetters, mapState } from "vuex";
|
||||
import { baseURL } from "@/utils/constants";
|
||||
import filesize from "filesize";
|
||||
import moment from "moment";
|
||||
import { files as api } from "@/api";
|
||||
|
||||
export default {
|
||||
name: 'item',
|
||||
name: "item",
|
||||
data: function() {
|
||||
return {
|
||||
touches: 0
|
||||
}
|
||||
};
|
||||
},
|
||||
props: ['name', 'isDir', 'url', 'type', 'size', 'modified', 'index'],
|
||||
props: ["name", "isDir", "url", "type", "size", "modified", "index"],
|
||||
computed: {
|
||||
...mapState(['selected', 'req']),
|
||||
...mapGetters(['selectedCount']),
|
||||
...mapState(["selected", "req", "jwt"]),
|
||||
...mapGetters(["selectedCount"]),
|
||||
isSelected() {
|
||||
return (this.selected.indexOf(this.index) !== -1)
|
||||
return this.selected.indexOf(this.index) !== -1;
|
||||
},
|
||||
icon() {
|
||||
if (this.isDir) return 'folder'
|
||||
if (this.type === 'image') return 'insert_photo'
|
||||
if (this.type === 'audio') return 'volume_up'
|
||||
if (this.type === 'video') return 'movie'
|
||||
return 'insert_drive_file'
|
||||
if (this.isDir) return "folder";
|
||||
if (this.type === "image") return "insert_photo";
|
||||
if (this.type === "audio") return "volume_up";
|
||||
if (this.type === "video") return "movie";
|
||||
return "insert_drive_file";
|
||||
},
|
||||
canDrop() {
|
||||
if (!this.isDir) return false
|
||||
if (!this.isDir) return false;
|
||||
|
||||
for (let i of this.selected) {
|
||||
if (this.req.items[i].url === this.url) {
|
||||
return false
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
return true;
|
||||
},
|
||||
compressUrl() {
|
||||
const path = this.url.replace(/^\/files\//, "");
|
||||
return `${baseURL}/api/compress/${path}?auth=${this.jwt}&inline=true`;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
...mapMutations(['addSelected', 'removeSelected', 'resetSelected']),
|
||||
...mapMutations(["addSelected", "removeSelected", "resetSelected"]),
|
||||
humanSize: function() {
|
||||
return filesize(this.size)
|
||||
return filesize(this.size);
|
||||
},
|
||||
humanTime: function() {
|
||||
return moment(this.modified).fromNow()
|
||||
return moment(this.modified).fromNow();
|
||||
},
|
||||
dragStart: function() {
|
||||
if (this.selectedCount === 0) {
|
||||
this.addSelected(this.index)
|
||||
return
|
||||
this.addSelected(this.index);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.isSelected) {
|
||||
this.resetSelected()
|
||||
this.addSelected(this.index)
|
||||
this.resetSelected();
|
||||
this.addSelected(this.index);
|
||||
}
|
||||
},
|
||||
dragOver: function(event) {
|
||||
if (!this.canDrop) return
|
||||
if (!this.canDrop) return;
|
||||
|
||||
event.preventDefault()
|
||||
let el = event.target
|
||||
event.preventDefault();
|
||||
let el = event.target;
|
||||
|
||||
for (let i = 0; i < 5; i++) {
|
||||
if (!el.classList.contains('item')) {
|
||||
el = el.parentElement
|
||||
if (!el.classList.contains("item")) {
|
||||
el = el.parentElement;
|
||||
}
|
||||
}
|
||||
|
||||
el.style.opacity = 1
|
||||
el.style.opacity = 1;
|
||||
},
|
||||
drop: function(event) {
|
||||
if (!this.canDrop) return
|
||||
event.preventDefault()
|
||||
if (!this.canDrop) return;
|
||||
event.preventDefault();
|
||||
|
||||
if (this.selectedCount === 0) return
|
||||
if (this.selectedCount === 0) return;
|
||||
|
||||
let items = []
|
||||
let items = [];
|
||||
|
||||
for (let i of this.selected) {
|
||||
items.push({
|
||||
from: this.req.items[i].url,
|
||||
to: this.url + this.req.items[i].name
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
api.move(items)
|
||||
api
|
||||
.move(items)
|
||||
.then(() => {
|
||||
this.$store.commit('setReload', true)
|
||||
this.$store.commit("setReload", true);
|
||||
})
|
||||
.catch(this.$showError)
|
||||
.catch(this.$showError);
|
||||
},
|
||||
click: function(event) {
|
||||
if (this.selectedCount !== 0) event.preventDefault()
|
||||
if (this.selectedCount !== 0) event.preventDefault();
|
||||
if (this.$store.state.selected.indexOf(this.index) !== -1) {
|
||||
this.removeSelected(this.index)
|
||||
return
|
||||
this.removeSelected(this.index);
|
||||
return;
|
||||
}
|
||||
|
||||
if (event.shiftKey) {
|
||||
let fi = 0
|
||||
let la = 0
|
||||
let fi = 0;
|
||||
let la = 0;
|
||||
|
||||
if (this.index > this.selected[0]) {
|
||||
fi = this.selected[0] + 1
|
||||
la = this.index
|
||||
fi = this.selected[0] + 1;
|
||||
la = this.index;
|
||||
} else {
|
||||
fi = this.index
|
||||
la = this.selected[0] - 1
|
||||
fi = this.index;
|
||||
la = this.selected[0] - 1;
|
||||
}
|
||||
|
||||
for (; fi <= la; fi++) {
|
||||
this.addSelected(fi)
|
||||
this.addSelected(fi);
|
||||
}
|
||||
|
||||
return
|
||||
return;
|
||||
}
|
||||
|
||||
if (!event.ctrlKey && !this.$store.state.multiple) this.resetSelected()
|
||||
this.addSelected(this.index)
|
||||
if (!event.ctrlKey && !this.$store.state.multiple) this.resetSelected();
|
||||
this.addSelected(this.index);
|
||||
},
|
||||
touchstart() {
|
||||
setTimeout(() => {
|
||||
this.touches = 0
|
||||
}, 300)
|
||||
this.touches = 0;
|
||||
}, 300);
|
||||
|
||||
this.touches++
|
||||
this.touches++;
|
||||
if (this.touches > 1) {
|
||||
this.open()
|
||||
this.open();
|
||||
}
|
||||
},
|
||||
open: function() {
|
||||
this.$router.push({path: this.url})
|
||||
}
|
||||
this.$router.push({ path: this.url });
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
@ -1,7 +1,13 @@
|
||||
<template>
|
||||
<div id="previewer">
|
||||
<div class="bar">
|
||||
<button @click="back" class="action" :title="$t('files.closePreview')" :aria-label="$t('files.closePreview')" id="close">
|
||||
<button
|
||||
@click="back"
|
||||
class="action"
|
||||
:title="$t('files.closePreview')"
|
||||
:aria-label="$t('files.closePreview')"
|
||||
id="close"
|
||||
>
|
||||
<i class="material-icons">close</i>
|
||||
</button>
|
||||
|
||||
@ -11,10 +17,22 @@
|
||||
<info-button></info-button>
|
||||
</div>
|
||||
|
||||
<button class="action" @click="prev" v-show="hasPrevious" :aria-label="$t('buttons.previous')" :title="$t('buttons.previous')">
|
||||
<button
|
||||
class="action"
|
||||
@click="prev"
|
||||
v-show="hasPrevious"
|
||||
:aria-label="$t('buttons.previous')"
|
||||
:title="$t('buttons.previous')"
|
||||
>
|
||||
<i class="material-icons">chevron_left</i>
|
||||
</button>
|
||||
<button class="action" @click="next" v-show="hasNext" :aria-label="$t('buttons.next')" :title="$t('buttons.next')">
|
||||
<button
|
||||
class="action"
|
||||
@click="next"
|
||||
v-show="hasNext"
|
||||
:aria-label="$t('buttons.next')"
|
||||
:title="$t('buttons.next')"
|
||||
>
|
||||
<i class="material-icons">chevron_right</i>
|
||||
</button>
|
||||
|
||||
@ -27,39 +45,39 @@
|
||||
v-for="(sub, index) in subtitles"
|
||||
:key="index"
|
||||
:src="sub"
|
||||
:label="'Subtitle ' + index" :default="index === 0">
|
||||
Sorry, your browser doesn't support embedded videos,
|
||||
but don't worry, you can <a :href="download">download it</a>
|
||||
:label="'Subtitle ' + index"
|
||||
:default="index === 0"
|
||||
/>Sorry, your browser doesn't support embedded videos,
|
||||
but don't worry, you can
|
||||
<a :href="download">download it</a>
|
||||
and watch it with your favorite video player!
|
||||
</video>
|
||||
<object v-else-if="req.extension == '.pdf'" class="pdf" :data="raw"></object>
|
||||
<a v-else-if="req.type == 'blob'" :href="download">
|
||||
<h2 class="message">{{ $t('buttons.download') }} <i class="material-icons">file_download</i></h2>
|
||||
<h2 class="message">
|
||||
{{ $t('buttons.download') }}
|
||||
<i class="material-icons">file_download</i>
|
||||
</h2>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapState } from 'vuex'
|
||||
import url from '@/utils/url'
|
||||
import { baseURL } from '@/utils/constants'
|
||||
import { files as api } from '@/api'
|
||||
import InfoButton from '@/components/buttons/Info'
|
||||
import DeleteButton from '@/components/buttons/Delete'
|
||||
import RenameButton from '@/components/buttons/Rename'
|
||||
import DownloadButton from '@/components/buttons/Download'
|
||||
import ExtendedImage from './ExtendedImage'
|
||||
import { mapState } from "vuex";
|
||||
import url from "@/utils/url";
|
||||
import { baseURL } from "@/utils/constants";
|
||||
import { files as api } from "@/api";
|
||||
import InfoButton from "@/components/buttons/Info";
|
||||
import DeleteButton from "@/components/buttons/Delete";
|
||||
import RenameButton from "@/components/buttons/Rename";
|
||||
import DownloadButton from "@/components/buttons/Download";
|
||||
import ExtendedImage from "./ExtendedImage";
|
||||
|
||||
const mediaTypes = [
|
||||
"image",
|
||||
"video",
|
||||
"audio",
|
||||
"blob"
|
||||
]
|
||||
const mediaTypes = ["image", "video", "audio", "blob"];
|
||||
|
||||
export default {
|
||||
name: 'preview',
|
||||
name: "preview",
|
||||
components: {
|
||||
InfoButton,
|
||||
DeleteButton,
|
||||
@ -69,92 +87,103 @@ export default {
|
||||
},
|
||||
data: function() {
|
||||
return {
|
||||
previousLink: '',
|
||||
nextLink: '',
|
||||
previousLink: "",
|
||||
nextLink: "",
|
||||
listing: null,
|
||||
subtitles: []
|
||||
}
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapState(['req', 'user', 'oldReq', 'jwt']),
|
||||
...mapState(["req", "user", "oldReq", "jwt"]),
|
||||
hasPrevious() {
|
||||
return (this.previousLink !== '')
|
||||
return this.previousLink !== "";
|
||||
},
|
||||
hasNext() {
|
||||
return (this.nextLink !== '')
|
||||
return this.nextLink !== "";
|
||||
},
|
||||
download() {
|
||||
return `${baseURL}/api/raw${this.req.path}?auth=${this.jwt}`
|
||||
return `${baseURL}/api/raw${this.req.path}?auth=${this.jwt}`;
|
||||
},
|
||||
compress() {
|
||||
if (this.type === 'image') {
|
||||
return `${baseURL}/api/compress${this.req.path}?auth=${this.jwt}`;
|
||||
} else {
|
||||
return `${baseURL}/api/raw${this.req.path}?auth=${this.jwt}`;
|
||||
}
|
||||
},
|
||||
raw() {
|
||||
return `${this.download}&inline=true`
|
||||
return `${this.compress}&inline=true`;
|
||||
}
|
||||
},
|
||||
async mounted() {
|
||||
window.addEventListener('keyup', this.key)
|
||||
window.addEventListener("keyup", this.key);
|
||||
|
||||
if (this.req.subtitles) {
|
||||
this.subtitles = this.req.subtitles.map(sub => `${baseURL}/api/raw${sub}?auth=${this.jwt}&inline=true`)
|
||||
this.subtitles = this.req.subtitles.map(
|
||||
sub => `${baseURL}/api/raw${sub}?auth=${this.jwt}&inline=true`
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
if (this.oldReq.items) {
|
||||
this.updateLinks(this.oldReq.items)
|
||||
this.updateLinks(this.oldReq.items);
|
||||
} else {
|
||||
const path = url.removeLastDir(this.$route.path)
|
||||
const res = await api.fetch(path)
|
||||
this.updateLinks(res.items)
|
||||
const path = url.removeLastDir(this.$route.path);
|
||||
const res = await api.fetch(path);
|
||||
this.updateLinks(res.items);
|
||||
}
|
||||
} catch (e) {
|
||||
this.$showError(e)
|
||||
this.$showError(e);
|
||||
}
|
||||
},
|
||||
beforeDestroy() {
|
||||
window.removeEventListener('keyup', this.key)
|
||||
window.removeEventListener("keyup", this.key);
|
||||
},
|
||||
methods: {
|
||||
back() {
|
||||
let uri = url.removeLastDir(this.$route.path) + '/'
|
||||
this.$router.push({ path: uri })
|
||||
let uri = url.removeLastDir(this.$route.path) + "/";
|
||||
this.$router.push({ path: uri });
|
||||
},
|
||||
prev() {
|
||||
this.$router.push({ path: this.previousLink })
|
||||
this.$router.push({ path: this.previousLink });
|
||||
},
|
||||
next() {
|
||||
this.$router.push({ path: this.nextLink })
|
||||
this.$router.push({ path: this.nextLink });
|
||||
},
|
||||
key(event) {
|
||||
event.preventDefault()
|
||||
event.preventDefault();
|
||||
|
||||
if (event.which === 13 || event.which === 39) { // right arrow
|
||||
if (this.hasNext) this.next()
|
||||
} else if (event.which === 37) { // left arrow
|
||||
if (this.hasPrevious) this.prev()
|
||||
if (event.which === 13 || event.which === 39) {
|
||||
// right arrow
|
||||
if (this.hasNext) this.next();
|
||||
} else if (event.which === 37) {
|
||||
// left arrow
|
||||
if (this.hasPrevious) this.prev();
|
||||
}
|
||||
},
|
||||
updateLinks(items) {
|
||||
for (let i = 0; i < items.length; i++) {
|
||||
if (items[i].name !== this.req.name) {
|
||||
continue
|
||||
continue;
|
||||
}
|
||||
|
||||
for (let j = i - 1; j >= 0; j--) {
|
||||
if (mediaTypes.includes(items[j].type)) {
|
||||
this.previousLink = items[j].url
|
||||
break
|
||||
this.previousLink = items[j].url;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (let j = i + 1; j < items.length; j++) {
|
||||
if (mediaTypes.includes(items[j].type)) {
|
||||
this.nextLink = items[j].url
|
||||
break
|
||||
this.nextLink = items[j].url;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
#listing h2 {
|
||||
margin: 0 0 0 0.5em;
|
||||
font-size: .9em;
|
||||
font-size: 0.9em;
|
||||
color: rgba(0, 0, 0, 0.38);
|
||||
font-weight: 500;
|
||||
}
|
||||
@ -22,7 +22,7 @@
|
||||
display: flex;
|
||||
flex-wrap: nowrap;
|
||||
color: #6f6f6f;
|
||||
transition: .1s ease background, .1s ease opacity;
|
||||
transition: 0.1s ease background, 0.1s ease opacity;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
}
|
||||
@ -52,6 +52,13 @@
|
||||
vertical-align: bottom;
|
||||
}
|
||||
|
||||
#listing .item img {
|
||||
width: 4em;
|
||||
height: 4em;
|
||||
margin-right: 0.1em;
|
||||
vertical-align: bottom;
|
||||
}
|
||||
|
||||
.message {
|
||||
text-align: center;
|
||||
font-size: 2em;
|
||||
@ -64,7 +71,7 @@
|
||||
|
||||
.message i {
|
||||
font-size: 2.5em;
|
||||
margin-bottom: .2em;
|
||||
margin-bottom: 0.2em;
|
||||
display: block;
|
||||
}
|
||||
|
||||
@ -75,14 +82,14 @@
|
||||
|
||||
#listing.mosaic .item {
|
||||
width: calc(33% - 1em);
|
||||
margin: .5em;
|
||||
margin: 0.5em;
|
||||
padding: 0.5em;
|
||||
border-radius: 0.2em;
|
||||
box-shadow: 0 1px 3px rgba(0, 0, 0, .06), 0 1px 2px rgba(0, 0, 0, .12);
|
||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.06), 0 1px 2px rgba(0, 0, 0, 0.12);
|
||||
}
|
||||
|
||||
#listing.mosaic .item:hover {
|
||||
box-shadow: 0 1px 3px rgba(0, 0, 0, .12), 0 1px 2px rgba(0, 0, 0, .24) !important;
|
||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 2px rgba(0, 0, 0, 0.24) !important;
|
||||
}
|
||||
|
||||
#listing.mosaic .header {
|
||||
@ -116,7 +123,7 @@
|
||||
display: none;
|
||||
}
|
||||
|
||||
#listing .item[aria-selected=true] {
|
||||
#listing .item[aria-selected="true"] {
|
||||
background: var(--blue) !important;
|
||||
color: #fff !important;
|
||||
}
|
||||
@ -129,6 +136,11 @@
|
||||
font-size: 2em;
|
||||
}
|
||||
|
||||
#listing.list .item div:first-of-type img {
|
||||
width: 2em;
|
||||
height: 2em;
|
||||
}
|
||||
|
||||
#listing.list .item div:last-of-type {
|
||||
width: calc(100% - 3em);
|
||||
display: flex;
|
||||
@ -151,14 +163,14 @@
|
||||
#listing.list .header i {
|
||||
font-size: 1.5em;
|
||||
vertical-align: middle;
|
||||
margin-left: .2em;
|
||||
margin-left: 0.2em;
|
||||
}
|
||||
|
||||
#listing.list .item.header {
|
||||
display: flex !important;
|
||||
background: #fafafa;
|
||||
z-index: 999;
|
||||
padding: .85em;
|
||||
padding: 0.85em;
|
||||
border: 0;
|
||||
border-bottom: 1px solid rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
@ -193,7 +205,7 @@
|
||||
|
||||
#listing.list .header i {
|
||||
opacity: 0;
|
||||
transition: .1s ease all;
|
||||
transition: 0.1s ease all;
|
||||
}
|
||||
|
||||
#listing.list .header p:hover i,
|
||||
@ -215,7 +227,7 @@
|
||||
height: 4em;
|
||||
padding: 0.5em 0.5em 0.5em 1em;
|
||||
justify-content: space-between;
|
||||
transition: .2s ease bottom;
|
||||
transition: 0.2s ease bottom;
|
||||
}
|
||||
|
||||
#listing #multiple-selection.active {
|
||||
|
||||
1
go.mod
1
go.mod
@ -16,6 +16,7 @@ require (
|
||||
github.com/maruel/natural v0.0.0-20180416170133-dbcb3e2e8cf1
|
||||
github.com/mholt/archiver v3.1.1+incompatible
|
||||
github.com/mitchellh/go-homedir v1.1.0
|
||||
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646
|
||||
github.com/nwaples/rardecode v1.0.0 // indirect
|
||||
github.com/pelletier/go-toml v1.6.0
|
||||
github.com/pierrec/lz4 v0.0.0-20190131084431-473cd7ce01a1 // indirect
|
||||
|
||||
2
go.sum
2
go.sum
@ -141,6 +141,8 @@ github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||
github.com/naoina/go-stringutil v0.1.0/go.mod h1:XJ2SJL9jCtBh+P9q5btrd/Ylo8XwT/h1USek5+NqSA0=
|
||||
github.com/naoina/toml v0.1.1/go.mod h1:NBIhNtsFMo3G2szEBne+bO4gS192HuIYRqfvOWb4i1E=
|
||||
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ=
|
||||
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8=
|
||||
github.com/nkovacs/streamquote v0.0.0-20170412213628-49af9bddb229 h1:E2B8qYyeSgv5MXpmzZXRNp8IAQ4vjxIjhpAf5hv/tAg=
|
||||
github.com/nkovacs/streamquote v0.0.0-20170412213628-49af9bddb229/go.mod h1:0aYXnNPJ8l7uZxf45rWW1a/uME32OF0rhiYGNQ2oF2E=
|
||||
github.com/nwaples/rardecode v1.0.0 h1:r7vGuS5akxOnR4JQSkko62RJ1ReCMXxQRPtxsiFMBOs=
|
||||
|
||||
@ -59,6 +59,7 @@ func NewHandler(store *storage.Storage, server *settings.Server) (http.Handler,
|
||||
api.Handle("/settings", monkey(settingsPutHandler, "")).Methods("PUT")
|
||||
|
||||
api.PathPrefix("/raw").Handler(monkey(rawHandler, "/api/raw")).Methods("GET")
|
||||
api.PathPrefix("/compress").Handler(monkey(compressHandler, "/api/compress")).Methods("GET")
|
||||
api.PathPrefix("/command").Handler(monkey(commandsHandler, "/api/command")).Methods("GET")
|
||||
api.PathPrefix("/search").Handler(monkey(searchHandler, "/api/search")).Methods("GET")
|
||||
|
||||
|
||||
126
http/preview.go
Normal file
126
http/preview.go
Normal file
@ -0,0 +1,126 @@
|
||||
package http
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/filebrowser/filebrowser/v2/files"
|
||||
"github.com/nfnt/resize"
|
||||
"image"
|
||||
"image/gif"
|
||||
"image/jpeg"
|
||||
"image/png"
|
||||
"io"
|
||||
"mime"
|
||||
"net/http"
|
||||
"net/url"
|
||||
)
|
||||
|
||||
var compressHandler = withUser(func(w http.ResponseWriter, r *http.Request, d *data) (int, error) {
|
||||
if !d.user.Perm.Download {
|
||||
return http.StatusAccepted, nil
|
||||
}
|
||||
|
||||
file, err := files.NewFileInfo(files.FileOptions{
|
||||
Fs: d.user.Fs,
|
||||
Path: r.URL.Path,
|
||||
Modify: d.user.Perm.Modify,
|
||||
Expand: true,
|
||||
Checker: d,
|
||||
})
|
||||
if err != nil {
|
||||
return errToStatus(err), err
|
||||
}
|
||||
|
||||
if file.IsDir || file.Type != "image" {
|
||||
return http.StatusNotFound, nil
|
||||
}
|
||||
|
||||
return compressFileHandler(w, r, file)
|
||||
})
|
||||
|
||||
func compressFileHandler(w http.ResponseWriter, r *http.Request, file *files.FileInfo) (int, error) {
|
||||
fd, err := file.Fs.Open(file.Path)
|
||||
if err != nil {
|
||||
return http.StatusInternalServerError, err
|
||||
}
|
||||
defer fd.Close()
|
||||
|
||||
if r.URL.Query().Get("inline") == "true" {
|
||||
w.Header().Set("Content-Disposition", "inline")
|
||||
} else {
|
||||
// As per RFC6266 section 4.3
|
||||
w.Header().Set("Content-Disposition", "attachment; filename*=utf-8''"+url.PathEscape(file.Name))
|
||||
}
|
||||
|
||||
buf, err := compressImageHandler(file, fd)
|
||||
if err != nil {
|
||||
return errToStatus(err), err
|
||||
}
|
||||
w.Header().Add("Content-Length", fmt.Sprintf("%d", buf.Len()))
|
||||
w.Header().Add("Content-Type", mime.TypeByExtension(file.Extension))
|
||||
io.Copy(w, buf)
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
func compressImageHandler(file *files.FileInfo, fd io.Reader) (*bytes.Buffer, error) {
|
||||
var (
|
||||
buf *bytes.Buffer
|
||||
m image.Image
|
||||
err error
|
||||
)
|
||||
|
||||
switch file.Extension {
|
||||
case ".jpg", ".jpeg":
|
||||
buf, m, err = compressImage(jpeg.Decode, fd)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = jpeg.Encode(buf, m, nil)
|
||||
break
|
||||
case ".png":
|
||||
buf, m, err = compressImage(png.Decode, fd)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = png.Encode(buf, m)
|
||||
break
|
||||
case ".gif":
|
||||
buf, m, err = compressImage(gif.Decode, fd)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = gif.Encode(buf, m, nil)
|
||||
break
|
||||
default:
|
||||
return nil, errors.New("extension is not supported")
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return buf, nil
|
||||
}
|
||||
|
||||
const maxSize = 1080
|
||||
|
||||
func compressImage(decode func(r io.Reader) (image.Image, error), fd io.Reader) (*bytes.Buffer, image.Image, error) {
|
||||
img, err := decode(fd)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
buf := bytes.NewBuffer([]byte{})
|
||||
width := img.Bounds().Dx()
|
||||
height := img.Bounds().Dy()
|
||||
if width > maxSize && width > height {
|
||||
width = maxSize
|
||||
height = 0
|
||||
} else if height > maxSize && height > width {
|
||||
width = 0
|
||||
height = maxSize
|
||||
} else {
|
||||
width = 0
|
||||
height = 0
|
||||
}
|
||||
m := resize.Resize(uint(width), uint(height), img, resize.Lanczos3)
|
||||
return buf, m, nil
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user