Compare commits
9 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d0b359561f | ||
|
|
453636dfe2 | ||
|
|
b1605aa6d3 | ||
|
|
23503b80a4 | ||
|
|
0d69fbd9a3 | ||
|
|
0d665e528f | ||
|
|
de0b8bb7b2 | ||
|
|
84da110085 | ||
|
|
6b0d49b1fc |
@@ -88,7 +88,7 @@ dockers:
|
||||
goarm: ''
|
||||
image_templates:
|
||||
- "filebrowser/filebrowser:alpine"
|
||||
- "filebrowser/filebrowser:{{ .Tag }}-apline"
|
||||
- "filebrowser/filebrowser:{{ .Tag }}-alpine"
|
||||
- "filebrowser/filebrowser:v{{ .Major }}-alpine"
|
||||
extra_files:
|
||||
- .docker.json
|
||||
|
||||
25
CHANGELOG.md
25
CHANGELOG.md
@@ -2,6 +2,31 @@
|
||||
|
||||
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
|
||||
|
||||
## [2.4.0](https://github.com/filebrowser/filebrowser/compare/v2.3.0...v2.4.0) (2020-07-07)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* full screen editor ([0d665e5](https://github.com/filebrowser/filebrowser/commit/0d665e528f880ceda0976ceed66070ac34de7969))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* add preview bypass for .gif files ([#1012](https://github.com/filebrowser/filebrowser/issues/1012)) ([453636d](https://github.com/filebrowser/filebrowser/commit/453636dfe2bbf177c74617862eb763485d4774bf))
|
||||
* prompt key shortcut conflict ([0d69fbd](https://github.com/filebrowser/filebrowser/commit/0d69fbd9a342aa2695859021df0c423e3ae4a4fa))
|
||||
|
||||
## [2.3.0](https://github.com/filebrowser/filebrowser/compare/v2.2.0...v2.3.0) (2020-06-26)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* add image thumbnails support ([#980](https://github.com/filebrowser/filebrowser/issues/980)) ([6b0d49b](https://github.com/filebrowser/filebrowser/commit/6b0d49b1fc8bdce89576ba91cc0b8ec594fcd625))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* typo in image_templates (apline -> alpine) ([#1005](https://github.com/filebrowser/filebrowser/issues/1005)) ([84da110](https://github.com/filebrowser/filebrowser/commit/84da11008516a371fc0446d97863dc14d337aa25))
|
||||
|
||||
## [2.2.0](https://github.com/filebrowser/filebrowser/compare/v2.1.2...v2.2.0) (2020-06-22)
|
||||
|
||||
|
||||
|
||||
@@ -124,6 +124,14 @@ nav > div {
|
||||
color: var(--textPrimary);
|
||||
}
|
||||
|
||||
#editor-container {
|
||||
background: var(--background);
|
||||
}
|
||||
|
||||
#editor-container .bar {
|
||||
background: var(--surfacePrimary);
|
||||
}
|
||||
|
||||
@media (max-width: 736px) {
|
||||
#file-selection {
|
||||
background: var(--surfaceSecondary) !important;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<header>
|
||||
<header v-if="!isEditor">
|
||||
<div>
|
||||
<button @click="openSidebar" :aria-label="$t('buttons.toggleSidebar')" :title="$t('buttons.toggleSidebar')" class="action">
|
||||
<i class="material-icons">menu</i>
|
||||
@@ -13,10 +13,6 @@
|
||||
<i class="material-icons">search</i>
|
||||
</button>
|
||||
|
||||
<button v-show="showSaveButton" :aria-label="$t('buttons.save')" :title="$t('buttons.save')" class="action" id="save-button">
|
||||
<i class="material-icons">save</i>
|
||||
</button>
|
||||
|
||||
<button @click="openMore" id="more" :aria-label="$t('buttons.more')" :title="$t('buttons.more')" class="action">
|
||||
<i class="material-icons">more_vert</i>
|
||||
</button>
|
||||
@@ -129,9 +125,6 @@ export default {
|
||||
showUpload () {
|
||||
return this.isListing && this.user.perm.create
|
||||
},
|
||||
showSaveButton () {
|
||||
return this.isEditor && this.user.perm.modify
|
||||
},
|
||||
showDownloadButton () {
|
||||
return this.isFiles && this.user.perm.download
|
||||
},
|
||||
|
||||
@@ -136,12 +136,6 @@ export default {
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
window.addEventListener("keydown", event => {
|
||||
if (event.keyCode === 27) {
|
||||
this.closeHovers()
|
||||
}
|
||||
})
|
||||
|
||||
this.$refs.result.addEventListener('scroll', event => {
|
||||
if (event.target.offsetHeight + event.target.scrollTop >= event.target.scrollHeight - 100) {
|
||||
this.resultsCount += 50
|
||||
|
||||
@@ -1,11 +1,37 @@
|
||||
<template>
|
||||
<form id="editor"></form>
|
||||
<div id="editor-container">
|
||||
<div class="bar">
|
||||
<button @click="back" :title="$t('files.closePreview')" :aria-label="$t('files.closePreview')" id="close" class="action">
|
||||
<i class="material-icons">close</i>
|
||||
</button>
|
||||
|
||||
<div class="title">
|
||||
<span>{{ req.name }}</span>
|
||||
</div>
|
||||
|
||||
<button @click="save" v-show="user.perm.modify" :aria-label="$t('buttons.save')" :title="$t('buttons.save')" id="save-button" class="action">
|
||||
<i class="material-icons">save</i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div id="breadcrumbs">
|
||||
<span><i class="material-icons">home</i></span>
|
||||
|
||||
<span v-for="(link, index) in breadcrumbs" :key="index">
|
||||
<span class="chevron"><i class="material-icons">keyboard_arrow_right</i></span>
|
||||
<span>{{ link.name }}</span>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<form id="editor"></form>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapState } from 'vuex'
|
||||
import { files as api } from '@/api'
|
||||
import buttons from '@/utils/buttons'
|
||||
import url from '@/utils/url'
|
||||
|
||||
import ace from 'ace-builds/src-min-noconflict/ace.js'
|
||||
import modelist from 'ace-builds/src-min-noconflict/ext-modelist.js'
|
||||
@@ -14,27 +40,52 @@ import { theme } from '@/utils/constants'
|
||||
|
||||
export default {
|
||||
name: 'editor',
|
||||
computed: {
|
||||
...mapState(['req'])
|
||||
},
|
||||
data: function () {
|
||||
return {}
|
||||
},
|
||||
computed: {
|
||||
...mapState(['req', 'user']),
|
||||
breadcrumbs () {
|
||||
let parts = this.$route.path.split('/')
|
||||
|
||||
if (parts[0] === '') {
|
||||
parts.shift()
|
||||
}
|
||||
|
||||
if (parts[parts.length - 1] === '') {
|
||||
parts.pop()
|
||||
}
|
||||
|
||||
let breadcrumbs = []
|
||||
|
||||
for (let i = 0; i < parts.length; i++) {
|
||||
breadcrumbs.push({ name: decodeURIComponent(parts[i]) })
|
||||
}
|
||||
|
||||
breadcrumbs.shift()
|
||||
|
||||
if (breadcrumbs.length > 3) {
|
||||
while (breadcrumbs.length !== 4) {
|
||||
breadcrumbs.shift()
|
||||
}
|
||||
|
||||
breadcrumbs[0].name = '...'
|
||||
}
|
||||
|
||||
return breadcrumbs
|
||||
}
|
||||
},
|
||||
created () {
|
||||
window.addEventListener('keydown', this.keyEvent)
|
||||
document.getElementById('save-button').addEventListener('click', this.save)
|
||||
},
|
||||
beforeDestroy () {
|
||||
window.removeEventListener('keydown', this.keyEvent)
|
||||
document.getElementById('save-button').removeEventListener('click', this.save)
|
||||
this.editor.destroy();
|
||||
},
|
||||
mounted: function () {
|
||||
mounted: function () {
|
||||
const fileContent = this.req.content || '';
|
||||
|
||||
this.editor = ace.edit('editor', {
|
||||
maxLines: 80,
|
||||
minLines: 20,
|
||||
value: fileContent,
|
||||
showPrintMargin: false,
|
||||
readOnly: this.req.type === 'textImmutable',
|
||||
@@ -48,6 +99,10 @@ export default {
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
back () {
|
||||
let uri = url.removeLastDir(this.$route.path) + '/'
|
||||
this.$router.push({ path: uri })
|
||||
},
|
||||
keyEvent (event) {
|
||||
if (!event.ctrlKey && !event.metaKey) {
|
||||
return
|
||||
|
||||
@@ -101,7 +101,7 @@ export default {
|
||||
components: { Item },
|
||||
data: function () {
|
||||
return {
|
||||
show: 50,
|
||||
showLimit: 50,
|
||||
uploading: {
|
||||
id: 0,
|
||||
count: 0,
|
||||
@@ -111,7 +111,7 @@ export default {
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapState(['req', 'selected', 'user']),
|
||||
...mapState(['req', 'selected', 'user', 'show']),
|
||||
nameSorted () {
|
||||
return (this.req.sorting.by === 'name')
|
||||
},
|
||||
@@ -139,14 +139,14 @@ export default {
|
||||
return { dirs, files }
|
||||
},
|
||||
dirs () {
|
||||
return this.items.dirs.slice(0, this.show)
|
||||
return this.items.dirs.slice(0, this.showLimit)
|
||||
},
|
||||
files () {
|
||||
let show = this.show - this.items.dirs.length
|
||||
let showLimit = this.showLimit - this.items.dirs.length
|
||||
|
||||
if (show < 0) show = 0
|
||||
if (showLimit < 0) showLimit = 0
|
||||
|
||||
return this.items.files.slice(0, show)
|
||||
return this.items.files.slice(0, showLimit)
|
||||
},
|
||||
nameIcon () {
|
||||
if (this.nameSorted && !this.ascOrdered) {
|
||||
@@ -194,7 +194,11 @@ export default {
|
||||
base64: function (name) {
|
||||
return window.btoa(unescape(encodeURIComponent(name)))
|
||||
},
|
||||
keyEvent (event) {
|
||||
keyEvent (event) {
|
||||
if (this.show !== null) {
|
||||
return
|
||||
}
|
||||
|
||||
if (!event.ctrlKey && !event.metaKey) {
|
||||
return
|
||||
}
|
||||
@@ -292,7 +296,7 @@ export default {
|
||||
},
|
||||
scrollEvent () {
|
||||
if ((window.innerHeight + window.scrollY) >= document.body.offsetHeight) {
|
||||
this.show += 50
|
||||
this.showLimit += 50
|
||||
}
|
||||
},
|
||||
dragEnter () {
|
||||
|
||||
@@ -13,7 +13,8 @@
|
||||
:aria-label="name"
|
||||
:aria-selected="isSelected">
|
||||
<div>
|
||||
<i class="material-icons">{{ icon }}</i>
|
||||
<img v-if="type==='image'" :src="thumbnailUrl">
|
||||
<i v-else class="material-icons">{{ icon }}</i>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
@@ -30,6 +31,7 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { baseURL } from '@/utils/constants'
|
||||
import { mapMutations, mapGetters, mapState } from 'vuex'
|
||||
import filesize from 'filesize'
|
||||
import moment from 'moment'
|
||||
@@ -44,7 +46,7 @@ export default {
|
||||
},
|
||||
props: ['name', 'isDir', 'url', 'type', 'size', 'modified', 'index'],
|
||||
computed: {
|
||||
...mapState(['selected', 'req', 'user']),
|
||||
...mapState(['selected', 'req', 'user', 'jwt']),
|
||||
...mapGetters(['selectedCount']),
|
||||
isSelected () {
|
||||
return (this.selected.indexOf(this.index) !== -1)
|
||||
@@ -69,6 +71,10 @@ export default {
|
||||
}
|
||||
|
||||
return true
|
||||
},
|
||||
thumbnailUrl () {
|
||||
const path = this.url.replace(/^\/files\//, '')
|
||||
return `${baseURL}/api/preview/thumb/${path}?auth=${this.jwt}&inline=true`
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
|
||||
@@ -86,8 +86,14 @@ export default {
|
||||
download () {
|
||||
return `${baseURL}/api/raw${this.req.path}?auth=${this.jwt}`
|
||||
},
|
||||
previewUrl () {
|
||||
if (this.req.type === 'image') {
|
||||
return `${baseURL}/api/preview/big${this.req.path}?auth=${this.jwt}`
|
||||
}
|
||||
return `${baseURL}/api/raw${this.req.path}?auth=${this.jwt}`
|
||||
},
|
||||
raw () {
|
||||
return `${this.download}&inline=true`
|
||||
return `${this.previewUrl}&inline=true`
|
||||
}
|
||||
},
|
||||
async mounted () {
|
||||
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
|
||||
@@ -184,6 +184,53 @@
|
||||
right: 0.5em;
|
||||
}
|
||||
|
||||
/* EDITOR */
|
||||
|
||||
#editor-container {
|
||||
background-color: #fafafa;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
z-index: 9999;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
#editor-container .bar {
|
||||
width: 100%;
|
||||
text-align: right;
|
||||
display: flex;
|
||||
padding: 0.5em;
|
||||
height: 3.7em;
|
||||
background-color: #fff;
|
||||
border-bottom: 1px solid rgba(0, 0, 0, 0.075);
|
||||
box-shadow: 0 0 5px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
#editor-container .title {
|
||||
margin-right: auto;
|
||||
padding: 0 1em;
|
||||
line-height: 2.7em;
|
||||
overflow: hidden;
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
#previewer .title span {
|
||||
font-size: 1.2em;
|
||||
}
|
||||
|
||||
#editor-container #editor {
|
||||
height: calc(100vh - 8.2em);
|
||||
}
|
||||
|
||||
#editor-container #breadcrumbs {
|
||||
height: 2.3em;
|
||||
padding: 0 1em;
|
||||
}
|
||||
|
||||
#editor-container #breadcrumbs span {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
/* * * * * * * * * * * * * * * *
|
||||
* PROMPT *
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div>
|
||||
<div id="breadcrumbs">
|
||||
<div id="breadcrumbs" v-if="isListing || error">
|
||||
<router-link to="/files/" :aria-label="$t('files.home')" :title="$t('files.home')">
|
||||
<i class="material-icons">home</i>
|
||||
</router-link>
|
||||
@@ -61,7 +61,8 @@ export default {
|
||||
'user',
|
||||
'reload',
|
||||
'multiple',
|
||||
'loading'
|
||||
'loading',
|
||||
'show'
|
||||
]),
|
||||
isPreview () {
|
||||
return !this.loading && !this.isListing && !this.isEditor
|
||||
@@ -158,10 +159,17 @@ export default {
|
||||
}
|
||||
},
|
||||
keyEvent (event) {
|
||||
// Esc!
|
||||
if (event.keyCode === 27) {
|
||||
this.$store.commit('closeHovers')
|
||||
if (this.show !== null) {
|
||||
// Esc!
|
||||
if (event.keyCode === 27) {
|
||||
this.$store.commit('closeHovers')
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Esc!
|
||||
if (event.keyCode === 27) {
|
||||
// If we're on a listing, unselect all
|
||||
// files and folders.
|
||||
if (this.isListing) {
|
||||
|
||||
1
go.mod
1
go.mod
@@ -8,6 +8,7 @@ require (
|
||||
github.com/caddyserver/caddy v1.0.3
|
||||
github.com/daaku/go.zipexe v1.0.1 // indirect
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible
|
||||
github.com/disintegration/imaging v1.6.2
|
||||
github.com/dsnet/compress v0.0.1 // indirect
|
||||
github.com/golang/snappy v0.0.1 // indirect
|
||||
github.com/gorilla/mux v1.7.3
|
||||
|
||||
4
go.sum
4
go.sum
@@ -43,6 +43,8 @@ github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
||||
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
|
||||
github.com/disintegration/imaging v1.6.2 h1:w1LecBlG2Lnp8B3jk5zSuNqd7b4DXhcjwek1ei82L+c=
|
||||
github.com/disintegration/imaging v1.6.2/go.mod h1:44/5580QXChDfwIclfc/PCwrr44amcmDAg8hxG0Ewe4=
|
||||
github.com/dsnet/compress v0.0.1 h1:PlZu0n3Tuv04TzpfPbrnI0HW/YwodEXDS+oPKahKF0Q=
|
||||
github.com/dsnet/compress v0.0.1/go.mod h1:Aw8dCMJ7RioblQeTqt88akK31OvO8Dhf5JflhBbQEHo=
|
||||
github.com/dsnet/golib v0.0.0-20171103203638-1ea166775780/go.mod h1:Lj+Z9rebOhdfkVLjJ8T6VcRQv3SXugXy999NBtR9aFY=
|
||||
@@ -239,6 +241,8 @@ golang.org/x/crypto v0.0.0-20190228161510-8dd112bcdc25/go.mod h1:djNgcEr1/C05ACk
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37 h1:cg5LA/zNPRzIXIWSCxQW10Rvpy94aQh3LT/ShoCpkHw=
|
||||
golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8 h1:hVwzHzIUGRjiF7EcUjqNxk3NCfkPxbDKRdnNE1Rpg0U=
|
||||
golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225 h1:kNX+jCowfMYzvlSvJu5pQWEmyWFrBXJ3PBy10xKMXK8=
|
||||
|
||||
@@ -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("/preview/{size}/{path:.*}").Handler(monkey(previewHandler, "/api/preview")).Methods("GET")
|
||||
api.PathPrefix("/command").Handler(monkey(commandsHandler, "/api/command")).Methods("GET")
|
||||
api.PathPrefix("/search").Handler(monkey(searchHandler, "/api/search")).Methods("GET")
|
||||
|
||||
|
||||
101
http/preview.go
Normal file
101
http/preview.go
Normal file
@@ -0,0 +1,101 @@
|
||||
package http
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"image"
|
||||
"net/http"
|
||||
|
||||
"github.com/disintegration/imaging"
|
||||
"github.com/gorilla/mux"
|
||||
|
||||
"github.com/filebrowser/filebrowser/v2/files"
|
||||
)
|
||||
|
||||
const (
|
||||
sizeThumb = "thumb"
|
||||
sizeBig = "big"
|
||||
)
|
||||
|
||||
type imageProcessor func(src image.Image) (image.Image, error)
|
||||
|
||||
var previewHandler = withUser(func(w http.ResponseWriter, r *http.Request, d *data) (int, error) {
|
||||
if !d.user.Perm.Download {
|
||||
return http.StatusAccepted, nil
|
||||
}
|
||||
vars := mux.Vars(r)
|
||||
size := vars["size"]
|
||||
if size != sizeBig && size != sizeThumb {
|
||||
return http.StatusNotImplemented, nil
|
||||
}
|
||||
|
||||
file, err := files.NewFileInfo(files.FileOptions{
|
||||
Fs: d.user.Fs,
|
||||
Path: "/" + vars["path"],
|
||||
Modify: d.user.Perm.Modify,
|
||||
Expand: true,
|
||||
Checker: d,
|
||||
})
|
||||
if err != nil {
|
||||
return errToStatus(err), err
|
||||
}
|
||||
|
||||
setContentDisposition(w, r, file)
|
||||
|
||||
switch file.Type {
|
||||
case "image":
|
||||
return handleImagePreview(w, r, file, size)
|
||||
default:
|
||||
return http.StatusNotImplemented, fmt.Errorf("can't create preview for %s type", file.Type)
|
||||
}
|
||||
})
|
||||
|
||||
func handleImagePreview(w http.ResponseWriter, r *http.Request, file *files.FileInfo, size string) (int, error) {
|
||||
format, err := imaging.FormatFromExtension(file.Extension)
|
||||
if err != nil {
|
||||
// Unsupported extensions directly return the raw data
|
||||
if err == imaging.ErrUnsupportedFormat {
|
||||
return rawFileHandler(w, r, file)
|
||||
}
|
||||
return errToStatus(err), err
|
||||
}
|
||||
|
||||
fd, err := file.Fs.Open(file.Path)
|
||||
if err != nil {
|
||||
return errToStatus(err), err
|
||||
}
|
||||
defer fd.Close()
|
||||
|
||||
if format == imaging.GIF && size == sizeBig {
|
||||
if _, err := rawFileHandler(w, r, file); err != nil { //nolint: govet
|
||||
return errToStatus(err), err
|
||||
}
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
var imgProcessor imageProcessor
|
||||
switch size {
|
||||
case sizeBig:
|
||||
imgProcessor = func(img image.Image) (image.Image, error) {
|
||||
return imaging.Fit(img, 1080, 1080, imaging.Lanczos), nil
|
||||
}
|
||||
case sizeThumb:
|
||||
imgProcessor = func(img image.Image) (image.Image, error) {
|
||||
return imaging.Thumbnail(img, 128, 128, imaging.Box), nil
|
||||
}
|
||||
default:
|
||||
return http.StatusBadRequest, fmt.Errorf("unsupported preview size %s", size)
|
||||
}
|
||||
|
||||
img, err := imaging.Decode(fd, imaging.AutoOrientation(true))
|
||||
if err != nil {
|
||||
return errToStatus(err), err
|
||||
}
|
||||
img, err = imgProcessor(img)
|
||||
if err != nil {
|
||||
return errToStatus(err), err
|
||||
}
|
||||
if imaging.Encode(w, img, format) != nil {
|
||||
return errToStatus(err), err
|
||||
}
|
||||
return 0, nil
|
||||
}
|
||||
16
http/raw.go
16
http/raw.go
@@ -58,6 +58,15 @@ func parseQueryAlgorithm(r *http.Request) (string, archiver.Writer, error) {
|
||||
}
|
||||
}
|
||||
|
||||
func setContentDisposition(w http.ResponseWriter, r *http.Request, file *files.FileInfo) {
|
||||
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))
|
||||
}
|
||||
}
|
||||
|
||||
var rawHandler = withUser(func(w http.ResponseWriter, r *http.Request, d *data) (int, error) {
|
||||
if !d.user.Perm.Download {
|
||||
return http.StatusAccepted, nil
|
||||
@@ -168,12 +177,7 @@ func rawFileHandler(w http.ResponseWriter, r *http.Request, file *files.FileInfo
|
||||
}
|
||||
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))
|
||||
}
|
||||
setContentDisposition(w, r, file)
|
||||
|
||||
http.ServeContent(w, r, file.Name, file.ModTime, fd)
|
||||
return 0, nil
|
||||
|
||||
Reference in New Issue
Block a user