I'm trying to upload an Image with the Uploader of Quasar via apollo-upload.
<q-uploader multiple :url="url" :upload-factory="uploadFactory"/> This is
how I implement the Uploader.
Here is the uploadFactory-Function:
uploadFactory (file, updateProgress) {
return(
this.$apollo.mutate({
mutation: gql`
mutation($file: Upload!) {
uploadFile(file: $file) {
filename
}
}
`,
variables: { file: file }
})
)
return new Promise(resolve, reject);
},
And on the serverside I got this:
async uploadFile (parent, { file } ) {
const { stream, filename, mimetype, encoding } = await file;
var fs = require('fs');
const id = 1;
const uploadDir = __dirname;
const path = `${uploadDir}/${filename}`
var wstream = fs.createWriteStream(path);
wstream.write(stream);
wstream.end();
return { stream, filename, mimetype, encoding };
},
},
So far for the Code. If I enter a new Image and press the upload-Button the uploadFactory has the img-src in file.__img. If sending the data to the server the __img-Object is totally empty: {}. I tried to send the file, just the file.__img, tried to copy the value, but it is empty every time.
Has Someone achieved this? Or is it even possible?
FYI: Some links, if you haven't heared of quasar or apollo yet: Quasar Uploader, Apollo Upload
I made a workaround, but it works for my needs.
Client:
<input-camera v-if="status=='S' && damage != null" ref="inputCamera" @photo="addFile" @popup="openPopup" @removePhoto="removeFile" v-bind:allowMultiple="true" v-bind:labelText="'Photos'" v-bind:id="id"/>
Above is how I load the module. You need to handle the @photo and @removePhoto where you included the module.
Here is the module I wrote:
<template>
<q-uploader v-bind:value="value" ref="uploader" inverted color="red-8" :float-
label="labelText" :multiple='allowMultiple' :url="url" hide-upload-progress send-
raw hide-upload-button class="col-3" @add="addFile" @remove:cancel="removeFile"/>
</template>
<script>
import { format } from 'quasar'
export default {
name: "input-camera",
inheritAttrs: false,
data() {
return {
url: '',
listenerIndex: null,
}
},
props: { value: Boolean, allowMultiple: Boolean, labelText: String, id: String },
methods: {
addFile(file)
{
var resizedFile = null;
file = file[0];
var that = this;
if(file.type.match(/image.*/)) {
// Load the image
var metaData = {};
var reader = new FileReader();
reader.onload = function (readerEvent) {
var image = new Image();
image.onload = function (imageEvent) {
// Resize the image
var canvas = document.createElement('canvas'),
max_size = 1024, // edit it, or get it from another param!
width = image.width,
height = image.height;
if (width > height) {
if (width > max_size) {
height *= max_size / width;
width = max_size;
}
} else {
if (height > max_size) {
width *= max_size / height;
height = max_size;
}
}
canvas.width = width;
canvas.height = height;
canvas.getContext('2d').drawImage(image, 0, 0, width, height);
var dataUrl = canvas.toDataURL('image/jpeg');
var type = 'image/jpeg';
//choose one of the above
resizedFile = dataUrl;
var nextId = that.$refs.uploader._data.files.length;
nextId = nextId - 1;
//add image metadata
metaData['lastModified'] = that.$refs.uploader._data.files[nextId].lastModified;
metaData['name'] = that.$refs.uploader._data.files[nextId].name;
metaData['size'] = that.$refs.uploader._data.files[nextId].size;
metaData['type'] = type;
//back to parent
that.addNextListener();
that.$emit('photo', resizedFile, that.id, metaData);
}
image.src = readerEvent.target.result;
}
reader.readAsDataURL(file);
}
},
removeFile(file)
{
var that = this;
var toFind = file.name;
var found = false;
var tempL = null;
for(let l = 0; l < this.$refs.uploader._data.files.length; l++)
{
if(this.$refs.uploader._data.files[l].name == toFind)
{
found = true;
tempL = l;
break;
}
}
if(found)
{
if(tempL != null) that.removeNextListener(tempL);
this.$emit('removePhoto', toFind, this.id);
}
},
updateUploaderImages: function(id, index, image){
// ToDo: below would work, but then we need other way to handle the original image
// this.$refs.uploader._data.files[index]['__img'].src = image;
},
addNextListener(nextListener)
{
var that = this;
if(nextListener == null)
{
nextListener = this.$refs.uploader._data.files.length-1;
const childs = this.$refs.uploader.$el.children[1].children[0].children[nextListener].children[0];
childs.id = 'image'+nextListener;
var selector = 'div#image'+nextListener;
this.listenerIndex = nextListener;
this.setListener(selector, 'click', that.openPopup, that.$refs.uploader.files[nextListener]);
}else{
for(let l = 0; l < nextListener.length; l++)
{
var listenerIndex = nextListener[l];
const childs = this.$refs.uploader.$el.children[1].children[0].children[listenerIndex].children[0];
childs.id = 'image'+listenerIndex;
var selector = 'div#image'+listenerIndex;
this.listenerIndex = listenerIndex;
this.setListener(selector, 'click', that.openPopup, that.$refs.uploader.files[listenerIndex]);
}
}
},
removeNextListener(index)
{
var that = this;
var nextListener = index;
var selector = 'div#image'+nextListener;
this.removeListener(selector, 'click', that.openPopup, that.$refs.uploader.files[nextListener]);
},
openPopup(img)
{
var that = this;
img = img['__img'].src;
this.$emit('popup', img , that.listenerIndex, that.id);
},
setListener(selector, event, callback, props)
{
var that = this;
var listenerElements = document.querySelectorAll(selector);
if(listenerElements != null)
{
for(var le=0; le<listenerElements.length; le++)
{
listenerElements[le].addEventListener(event, function(evt) {
if(callback != null) callback(props);
});
}
}
},
removeListener(selector, event, callback, props)
{
var that = this;
var listenerElements = document.querySelectorAll(selector);
if(listenerElements != null)
{
for(var le=0; le<listenerElements.length; le++)
{
listenerElements[le].removeEventListener(event, function(evt) {
if(callback != null) callback(props);
});
}
}
},
setImage(photos, id) {
var that = this;
var index = id - 1;
var listenerIndexes = [];
for(let l = 0; l < photos.length; l++)
{
var length = that.$refs.uploader._data.files.length;
var img = new Image;
// img.src = photos[l][id];
img.src = photos[l][l+1];
var metadata = photos[l].metaData;
var imgObject = {};
imgObject.lastModified = metadata.lastModified;
var DateTime = new Date(metadata.lastModified);
imgObject.lastModifiedDate = DateTime;
imgObject.name = metadata.name;
imgObject.size = metadata.size;
imgObject.type = metadata.type;
imgObject.webkitRelativePath = "";
imgObject.__doneUploading = false;
imgObject.__failed = false;
imgObject.__img = img;
imgObject.__progress = 0;
imgObject.__size = (metadata.size / 1024)+" kB";
imgObject.__timestamp = metadata.lastModified;
imgObject.__uploaded = false;
this.$refs.uploader._data.files[length] = imgObject;
this.$refs.uploader.totalSize = this.$refs.uploader.totalSize +
imgObject.size;
this.$refs.uploader.queue.push(imgObject);
this.$refs.uploader.expanded = true;
listenerIndexes.push(length);
}
setTimeout(function(){
that.addNextListener(listenerIndexes);
},50);
},
},
computed: {
inputListeners: function () {
var vm = this
// `Object.assign` merges objects together to form a new object
return Object.assign({},
// We add all the listeners from the parent
this.$listeners,
// Then we can add custom listeners or override the
// behavior of some listeners.
{
// This ensures that the component works with v-model
input: function (event) {
vm.$emit('input', event);
//if(!isNaN(event)) vm.$emit('input', event.toString().replace('.',''))
//else return event.slice(0, -1);
}
}
)
}
},
}
</script>
It also includes downscaling and sets Clicklisteners for the images, so you can open it inside of a popup. And you are able to insert an image by datauri back into the uploader, if you get it back from the server.
Then I just add the imageData to a json-object and send it to server, where it gets handled.
Hopefully it helps you out a bit.
PS: I disabled the upload-button, so on each add the photoData gets returned and i pushed it into an array and started the sending of the data on another button in UI.