使用 apollo-upload (GraphQL) 的 Quasar Framework (Vue) 上传器
Quasar Framework (Vue) Uploader using apollo-upload (GraphQL)
我正在尝试通过 apollo-upload 使用 Quasar 的上传器上传图像。
<q-uploader multiple :url="url" :upload-factory="uploadFactory"/> This is
how I implement the Uploader.
这是 uploadFactory 函数:
uploadFactory (file, updateProgress) {
return(
this.$apollo.mutate({
mutation: gql`
mutation($file: Upload!) {
uploadFile(file: $file) {
filename
}
}
`,
variables: { file: file }
})
)
return new Promise(resolve, reject);
},
在服务器端我得到了这个:
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 };
},
},
代码到此为止。
如果我输入一个新图像并按下上传按钮,uploadFactory 在 file.__img 中有 img-src。如果将数据发送到服务器,__img-Object 完全是空的:{}。我试图发送文件,只是 file.__img,试图复制值,但每次都是空的。
有人做到了吗?或者有可能吗?
仅供参考:一些链接,如果您还没有听说过类星体或阿波罗:
Quasar Uploader, Apollo Upload
我做了一个解决方法,但它可以满足我的需要。
客户:
<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"/>
以上是我加载模块的方式。您需要处理包含模块的 @photo 和 @removePhoto。
这是我写的模块:
<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>
它还包括缩小比例并为图像设置 Clicklisteners,因此您可以在弹出窗口中打开它。如果您从服务器取回图像,您可以通过 datauri 将图像插入回上传器。
然后我将 imageData 添加到 json-object 并将其发送到服务器,在那里它得到处理。
希望对您有所帮助。
PS:我禁用了上传按钮,所以在每次添加时都会返回照片数据,我将它推入一个数组并开始在 UI 中的另一个按钮上发送数据。
我正在尝试通过 apollo-upload 使用 Quasar 的上传器上传图像。
<q-uploader multiple :url="url" :upload-factory="uploadFactory"/> This is
how I implement the Uploader.
这是 uploadFactory 函数:
uploadFactory (file, updateProgress) {
return(
this.$apollo.mutate({
mutation: gql`
mutation($file: Upload!) {
uploadFile(file: $file) {
filename
}
}
`,
variables: { file: file }
})
)
return new Promise(resolve, reject);
},
在服务器端我得到了这个:
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 };
},
},
代码到此为止。 如果我输入一个新图像并按下上传按钮,uploadFactory 在 file.__img 中有 img-src。如果将数据发送到服务器,__img-Object 完全是空的:{}。我试图发送文件,只是 file.__img,试图复制值,但每次都是空的。
有人做到了吗?或者有可能吗?
仅供参考:一些链接,如果您还没有听说过类星体或阿波罗: Quasar Uploader, Apollo Upload
我做了一个解决方法,但它可以满足我的需要。
客户:
<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"/>
以上是我加载模块的方式。您需要处理包含模块的 @photo 和 @removePhoto。
这是我写的模块:
<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>
它还包括缩小比例并为图像设置 Clicklisteners,因此您可以在弹出窗口中打开它。如果您从服务器取回图像,您可以通过 datauri 将图像插入回上传器。
然后我将 imageData 添加到 json-object 并将其发送到服务器,在那里它得到处理。
希望对您有所帮助。
PS:我禁用了上传按钮,所以在每次添加时都会返回照片数据,我将它推入一个数组并开始在 UI 中的另一个按钮上发送数据。