Progress on storing user images
This commit is contained in:
@@ -9,9 +9,10 @@ const UserImages = createS3FilesCollection({
|
||||
return 'Please upload with size equal or less than 10MB';
|
||||
}
|
||||
// Allow common image extensions
|
||||
if (/gif|png|jpe?g|webp/i.test(file.extension || '')) {
|
||||
if (!/gif|png|jpe?g|webp/i.test(file.extension || '')) {
|
||||
return 'Please upload an image file only';
|
||||
}
|
||||
return true
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -47,6 +47,7 @@ if (Meteor.isServer && Meteor.settings.useS3) {
|
||||
collectionName,
|
||||
storagePath,
|
||||
onBeforeUpload,
|
||||
onAfterUpload,
|
||||
debug = Meteor.isProduction,
|
||||
allowClientCode = false,
|
||||
}){
|
||||
@@ -54,7 +55,10 @@ if (Meteor.isServer && Meteor.settings.useS3) {
|
||||
collectionName,
|
||||
storagePath,
|
||||
onBeforeUpload,
|
||||
onAfterUpload(fileRef){
|
||||
onAfterUpload(fileRef) {
|
||||
// Call the provided afterUpload hook first
|
||||
onAfterUpload(fileRef);
|
||||
|
||||
// Start moving files to AWS:S3
|
||||
// after fully received by the Meteor server
|
||||
|
||||
|
||||
8
app/imports/server/imageProcessing/createThumbnail.js
Normal file
8
app/imports/server/imageProcessing/createThumbnail.js
Normal file
@@ -0,0 +1,8 @@
|
||||
import * as sharp from 'sharp';
|
||||
|
||||
export default async function createThumbnail(image) {
|
||||
await sharp(image)
|
||||
.resize(320, 240)
|
||||
.png()
|
||||
.toBuffer();
|
||||
}
|
||||
@@ -11,3 +11,4 @@ import '/imports/server/publications/ownedDocuments.js';
|
||||
import '/imports/server/publications/archivedCreatures.js';
|
||||
import '/imports/server/publications/searchLibraryNodes.js';
|
||||
import '/imports/server/publications/archiveFiles.js';
|
||||
import '/imports/server/publications/userImages.js';
|
||||
|
||||
7
app/imports/server/publications/userImages.js
Normal file
7
app/imports/server/publications/userImages.js
Normal file
@@ -0,0 +1,7 @@
|
||||
import UserImages from '/imports/api/files/UserImages.js';
|
||||
|
||||
Meteor.publish('userImages', function () {
|
||||
return UserImages.find({
|
||||
userId: this.userId,
|
||||
}).cursor;
|
||||
});
|
||||
77
app/imports/ui/components/ImageUploadInput.vue
Normal file
77
app/imports/ui/components/ImageUploadInput.vue
Normal file
@@ -0,0 +1,77 @@
|
||||
<template>
|
||||
<div class="file-upload-input">
|
||||
<v-file-input
|
||||
v-model="file"
|
||||
v-bind="$attrs"
|
||||
accept="image/*"
|
||||
/>
|
||||
<v-progress-linear
|
||||
:progress="progress"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="js">
|
||||
import UserImages from '/imports/api/files/UserImages.js';
|
||||
|
||||
export default {
|
||||
data(){return {
|
||||
progress: 0,
|
||||
file: undefined,
|
||||
uploadingInProgress: false,
|
||||
}},
|
||||
watch: {
|
||||
file(file){
|
||||
if (!file) return;
|
||||
let self = this;
|
||||
let uploadInstance = UserImages.insert({
|
||||
file: file,
|
||||
/*meta: {
|
||||
userId: Meteor.userId() // Optional, used to check on server for file tampering
|
||||
},*/
|
||||
chunkSize: 'dynamic',
|
||||
allowWebWorkers: true // If you see issues with uploads, change this to false
|
||||
}, false)
|
||||
|
||||
// These are the event functions, don't need most of them, it shows where we are in the process
|
||||
uploadInstance.on('start', function () {
|
||||
console.log('Starting');
|
||||
this.uploadingInProgress = true;
|
||||
});
|
||||
|
||||
uploadInstance.on('end', function (error, fileObj) {
|
||||
console.log('On end File Object: ', fileObj);
|
||||
this.uploadingInProgress = false;
|
||||
});
|
||||
|
||||
uploadInstance.on('uploaded', function (error, fileObj) {
|
||||
console.log('uploaded: ', fileObj);
|
||||
|
||||
// Remove the file from the input box
|
||||
self.file = undefined;
|
||||
|
||||
// Reset our state for the next file
|
||||
self.uploadingInProgress = false;
|
||||
self.progress = 0;
|
||||
});
|
||||
|
||||
uploadInstance.on('error', function (error, fileObj) {
|
||||
console.log('Error during upload: ' + error, fileObj)
|
||||
});
|
||||
|
||||
uploadInstance.on('progress', function (progress, fileObj) {
|
||||
console.log('Upload Percentage: ' + progress, fileObj)
|
||||
// Update our progress bar
|
||||
self.progress = progress;
|
||||
});
|
||||
|
||||
uploadInstance.start(); // Must manually start the upload
|
||||
}
|
||||
},
|
||||
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
</style>
|
||||
56
app/imports/ui/files/UserImageCard.vue
Normal file
56
app/imports/ui/files/UserImageCard.vue
Normal file
@@ -0,0 +1,56 @@
|
||||
<template>
|
||||
<v-card
|
||||
:data-id="`${model._id}-archive-card`"
|
||||
tile
|
||||
>
|
||||
<v-img
|
||||
:src="model.link"
|
||||
:aspect-ratio="1.4"
|
||||
class="white--text align-end"
|
||||
>
|
||||
<v-card-title>
|
||||
{{ model.name }}
|
||||
</v-card-title>
|
||||
<v-card-subtitle>
|
||||
{{ model.size }}
|
||||
</v-card-subtitle>
|
||||
</v-img>
|
||||
<v-card-actions>
|
||||
<v-flex />
|
||||
<v-btn
|
||||
icon
|
||||
@click="remove"
|
||||
>
|
||||
<v-icon>mdi-delete</v-icon>
|
||||
</v-btn>
|
||||
<v-btn
|
||||
icon
|
||||
:href="`${model.link}?download=true`"
|
||||
>
|
||||
<v-icon>mdi-download</v-icon>
|
||||
</v-btn>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
</template>
|
||||
|
||||
<script lang="js">
|
||||
import { snackbar } from '/imports/ui/components/snackbars/SnackbarQueue.js';
|
||||
|
||||
export default {
|
||||
props: {
|
||||
model: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
data(){return {
|
||||
restoreLoading: false,
|
||||
removeLoading: false,
|
||||
}},
|
||||
methods: {
|
||||
remove(){
|
||||
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
@@ -7,65 +7,103 @@
|
||||
<file-storage-stats />
|
||||
</v-row>
|
||||
<v-row dense>
|
||||
<v-col cols="12">
|
||||
<v-subheader> Archived Characters </v-subheader>
|
||||
</v-col>
|
||||
<template v-if="archiveFiles && archiveFiles.length">
|
||||
<v-col cols="12">
|
||||
<v-subheader> Archived Characters </v-subheader>
|
||||
</v-col>
|
||||
<v-col
|
||||
v-for="file in archiveFiles"
|
||||
:key="file._id"
|
||||
cols="12"
|
||||
sm="6"
|
||||
md="4"
|
||||
lg="3"
|
||||
xl="2"
|
||||
>
|
||||
<archive-file-card :model="file" />
|
||||
</v-col>
|
||||
</template>
|
||||
<v-col
|
||||
key="upload"
|
||||
cols="12"
|
||||
sm="6"
|
||||
md="4"
|
||||
lg="3"
|
||||
xl="2"
|
||||
class="layout column justify-center"
|
||||
>
|
||||
<input
|
||||
ref="archiveFileInput"
|
||||
type="file"
|
||||
accept=".json"
|
||||
style="display: none;"
|
||||
@input="inputArchiveFile"
|
||||
>
|
||||
<v-btn
|
||||
outlined
|
||||
style="height: 100%; width: 100%;"
|
||||
:color="archiveFileError ? 'error' : undefined"
|
||||
@click="$refs.archiveFileInput.click()"
|
||||
>
|
||||
<v-icon left>
|
||||
mdi-file-upload-outline
|
||||
</v-icon>
|
||||
<template v-if="archiveFileError">
|
||||
{{ archiveFileError }}
|
||||
</template>
|
||||
<template v-else>
|
||||
Upload archive
|
||||
</template>
|
||||
</v-btn>
|
||||
</v-col>
|
||||
</v-row>
|
||||
<v-row dense>
|
||||
<v-col cols="12">
|
||||
<v-subheader> Images </v-subheader>
|
||||
</v-col>
|
||||
<template v-if="userImages && userImages.length">
|
||||
<v-col
|
||||
key="upload"
|
||||
v-for="file in userImages"
|
||||
:key="file._id"
|
||||
cols="12"
|
||||
sm="6"
|
||||
md="4"
|
||||
lg="3"
|
||||
class="layout column justify-center"
|
||||
xl="2"
|
||||
>
|
||||
<input
|
||||
ref="archiveFileInput"
|
||||
type="file"
|
||||
accept=".json"
|
||||
style="display: none;"
|
||||
@input="inputArchiveFile"
|
||||
>
|
||||
<v-btn
|
||||
outlined
|
||||
style="height: 100%; width: 100%;"
|
||||
:color="archiveFileError ? 'error' : undefined"
|
||||
@click="$refs.archiveFileInput.click()"
|
||||
>
|
||||
<v-icon left>
|
||||
mdi-file-upload-outline
|
||||
</v-icon>
|
||||
<template v-if="archiveFileError">
|
||||
{{ archiveFileError }}
|
||||
</template>
|
||||
<template v-else>
|
||||
Upload archive
|
||||
</template>
|
||||
</v-btn>
|
||||
<user-image-card :model="file" />
|
||||
</v-col>
|
||||
</template>
|
||||
<v-col
|
||||
key="image-upload"
|
||||
cols="12"
|
||||
sm="6"
|
||||
md="4"
|
||||
lg="3"
|
||||
xl="2"
|
||||
class="layout column justify-center"
|
||||
>
|
||||
<image-upload-input />
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-container>
|
||||
</template>
|
||||
|
||||
<script lang="js">
|
||||
import ArchiveCreatureFiles from '/imports/api/creature/archive/ArchiveCreatureFiles.js';
|
||||
import UserImages from '/imports/api/files/UserImages.js';
|
||||
import prettyBytes from 'pretty-bytes';
|
||||
import ArchiveFileCard from '/imports/ui/files/ArchiveFileCard.vue';
|
||||
import FileStorageStats from '/imports/ui/files/FileStorageStats.vue';
|
||||
import ImageUploadInput from '/imports/ui/components/ImageUploadInput.vue';
|
||||
import UserImageCard from '/imports/ui/files/UserImageCard.vue';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
ArchiveFileCard,
|
||||
FileStorageStats,
|
||||
ImageUploadInput,
|
||||
UserImageCard,
|
||||
},
|
||||
data(){ return {
|
||||
updateStorageUsedLoading: false,
|
||||
@@ -76,9 +114,10 @@ export default {
|
||||
$subscribe: {
|
||||
'archiveCreatureFiles': [],
|
||||
'characterList': [],
|
||||
'userImages': [],
|
||||
},
|
||||
archiveFiles() {
|
||||
var userId = Meteor.userId();
|
||||
const userId = Meteor.userId();
|
||||
return ArchiveCreatureFiles.find(
|
||||
{
|
||||
userId,
|
||||
@@ -91,6 +130,20 @@ export default {
|
||||
return f;
|
||||
});
|
||||
},
|
||||
userImages() {
|
||||
const userId = Meteor.userId();
|
||||
return UserImages.find({
|
||||
userId
|
||||
}, {
|
||||
sort: {
|
||||
size: -1
|
||||
},
|
||||
}).map(f => {
|
||||
f.size = prettyBytes(f.size);
|
||||
f.link = UserImages.link(f);
|
||||
return f;
|
||||
});
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
inputArchiveFile(){
|
||||
|
||||
Reference in New Issue
Block a user