Vue.js read blob as object/string to app-variable - javascript

I have a vue app with
data: function() {
return {
modules: [],
...
And method:
methods: {
submitted: function(){
...
axios({method: 'get',
url: 'http://' + document.location.host + '/api/execute?commands=' + encodeURI(JSON.stringify(commands)),
responseType: "blob"}).then(response => {
if (response.headers['content-type'] === 'image/jpeg'){
this.modules[current]['results']['image']['visible'] = true
this.modules[current]['results']['text']['visible'] = false
const url = window.URL.createObjectURL(response.data)
this.modules[current]['results']['image']['value'] = url
}
else{
this.modules[current]['results']['image']['visible'] = false
this.modules[current]['results']['text']['visible'] = true
var reader = new FileReader();
reader.onload = function(e) {
// reader.result contains the contents of blob as a typed array
console.log(e)
this.modules[current]['results']['text']['value'] = reader.result
}
reader.readAsArrayBuffer(response.data);
}
...
This should request some data from server and show it as text or image depending on server response.
*v-for module in modules earlier*
<div class="resultText" :visibility=module.results.text.visible>
{{ module.results.text.value }}
</div>
<div class="resultImage" :visibility=module.results.image.visible>
<img :src=module.results.image.value>
</div>
But the method gives js error when text returned: TypeError: this.modules is undefined
The error in this string:
this.modules[current]['results']['text']['value'] = reader.result
With images it works fine.
With self.modules it gives same: TypeError: self.modules is undefined

Solved the problem with:
response.data.text().then(text => {
this.modules[current]['results']['text']['value'] = text
});

Related

Load multipage PDF into vue.js page using vue-pdf

I'm trying to load a multipage pdf from an API into vue.js using the vue-pdf library.
I have can successfully load the pdf and render the 1st page as described in this earlier question here
I've followed the instructions given in The Documentation and tried a with / without using a loadingTask and using different options for the number of pages.
I've attached the code below. this version shows no page number and no pdf. The console has the "loadPdf called" and "success" log messages and no errors.
<template>
<div>
<h3>page count is {{ numPages }}</h3>
<pdf v-for="i in numPages" :key="i" :page="i" :src="pdfsrc"></pdf>
</div>
</template>
<script>
import pdf from "vue-pdf";
import axios from "axios";
import ActiveDirectoryService from "#/services/ActiveDirectoryService.js";
export default {
components: {
pdf
},
props: {
exportId: String
},
data() {
return {
pdfsrc: null,
numPages: null,
objectUrl: null
};
},
mounted() {
if (this.pdfsrc === null) return;
this.pdfsrc.promise.then(pdf => {
this.numPages = pdf.numPages;
});
},
methods: {
loadPdf() {
console.log("loadPdf called");
return axios
.get(`${process.env.VUE_APP_API_INTRANET}/pdf`, {
responseType: "blob"
})
.then(response => {
console.log("Success", response);
const blob = new Blob([response.data]);
const objectUrl = URL.createObjectURL(blob);
this.pdfsrc = pdf.createLoadingTask(objectUrl);
})
.catch(console.error); //
},
clearBuffer() {
if (this.objectUrl !== null) {
URL.revokeObjectURL(this.objectUrl);
this.pdfsrc = null;
this.objectUrl = null;
}
}
},
watch: {
exportId: function(oldVal, newVal) {
console.log("id Changed api for pdf.");
this.loadPdf();
}
},
created() {
console.log("created with id ", this.exportId);
this.loadPdf();
},
beforeDestroy() {
this.clearBuffer();
}
};
</script>
<style scoped></style>
I'm testing with a small 3 page pdf at 46kb in total. The closest I have come to getting this to work is to hard code the number of pages to 3 which renders the pdf correctly. eg
<pdf v-for="i in 3" :key="i" :page="i" :src="pdfsrc"></pdf>
It works but obviously not a solution!
I've also tried other ways to load 'numPages' such as adding this to pdf
#num-pages="numPages = $event"
and using a computed that returns this.pdf.numPages
but so far all have failed.
I've also tried moving the promise to get numpages into the loadPdf method like so
.then(response => {
console.log("Success", response);
const blob = new Blob([response.data]);
const objectUrl = URL.createObjectURL(blob);
this.pdfsrc = pdf.createLoadingTask(objectUrl);
this.pdfsrc.promise.then(pdf => {
this.numPages = pdf.numPages;
});
})
.catch(console.error); //
which also doesn't work.
Update
I've peppered the code with log statements as requested. All looks like it should be working.
.then(response => {
console.log("Success", response);
const blob = new Blob([response.data]);
const objectUrl = URL.createObjectURL(blob);
this.pdfsrc = pdf.createLoadingTask(objectUrl);
console.log("this.pdfsrc.promise", this.pdfsrc.promise);
this.pdfsrc.promise.then(pdf => {
console.log("pdf in callback", pdf);
this.numPages = pdf.numPages;
console.log(
`this.numPages: ${this.numPages} pdf.numPages: ${pdf.numPages} `
);
console.log(this);
});
})
Update 2
I've cleaned up my example and upon removing this line
#num-pages="numPages = $event"
from the template it all started working! I've traced back through the changes in code and it seems that wrapping the object url in a loadingTask and moving the promise to the loadPdf() method is what fixed it.

Upload Multiple images in Laravel using Vue.js

I'm trying to upload image using vue.js in Laravel for that i'm using this link
https://jsfiddle.net/b412ruzo/
to upload image using vue.js and when i submit the form i'm getting following image in files array
now issue is that i cannot get this file array in laravel controller
when i print
$request->file('files')
in my controller i am getting null.
and when i print $request->input('files') this is the result, an empty array
Any help regarding this issue is highly appreciated.
Code Snippet :
data() {
return {
rawData: [],
formData: new Form({
files:[],
})
..
const header = {
Authorization: "Bearer " + this.token,
};
this.formData
.post(APP_URL + `/api/post`, { headers: header })
.then((response) => {
}
not sure you can send ajax request via this.formData.post
try this
new Vue({
el: "#app",
data() {
return {
option: {
maxFileCount: 3
},
files:[],
rawData: [],
}
},
methods: {
loaddropfile: function(e) {
e.preventDefault()
e.stopPropagation()
alert('ok')
console.log(e)
},
openinput: function() {
document.getElementById("vue-file-upload-input").click();
},
addImage: function(e) {
const tmpFiles = e.target.files
if (tmpFiles.length === 0) {
return false;
}
const file = tmpFiles[0]
this.files.push(file)
const self = this
const reader = new FileReader()
reader.onload = function(e) {
self.rawData.push(e.target.result)
}
reader.readAsDataURL(file)
},
removeFile: function(index) {
this.files.splice(index, 1)
this.rawData.splice(index, 1)
document.getElementById("vue-file-upload-input").value = null
},
upload: function() {
alert('Check console to see uploads')
console.log(this.files)
axios.post(`${APP_URL}/api/post`,{files:this.files},{ headers: header })
.then((response) => {});
}
},
mounted(){
}
})
it will send your form data to files key so you can get all the files via $request->file('files')

Progress callback not updating during progress

I have written some code that reads a local text file and compresses it into a zip file using JSZip library.
I want to provide the user a progress bar as shown in this demo: https://stuk.github.io/jszip/documentation/examples/downloader.html
zip.generateAsync()method has a callback method but what happens is the file fully compresses then it send all the ticks at once at the end (check console output in code below using large enough file to cause some delay).
I can't understand what is blocking the callbacks until the process finishes. Can anyone help?
Here is my code:
var statusEl = document.getElementById("status");
const fileSelector = document.getElementById("file-selector");
fileSelector.addEventListener("change", (event) => {
readFile(event);
});
function readFile(event) {
statusEl.innerHTML = "Loading file";
const fileList = event.target.files;
console.log(fileList[0]);
const reader = new FileReader();
reader.addEventListener("load", (event) => {
statusEl.innerHTML = "Compressing file";
zipFile(event.target.result);
});
reader.readAsArrayBuffer(fileList[0]);
}
function zipFile(result) {
var zip = new JSZip();
zip.file("myfile.txt", result);
zip
.generateAsync(
{
type: "blob",
compression: "DEFLATE",
compressionOptions: {
level: 6
}
},
updateCallback
)
.then(function (content) {
saveAs(content, "example.zip");
});
}
function updateCallback(metaData) {
statusEl.innerHTML = metaData.percent.toFixed(2) + " %";
console.log(metaData.percent.toFixed(2) + " %");
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jszip/3.5.0/jszip.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/FileSaver.js/2.0.2/FileSaver.min.js"></script>
<label for="avatar">Choose a file:</label>
<input type="file"
id="file-selector" name="file-selector" accept=".txt">
<div id="status"></div>
JavaScript execution and page rendering are done in the same execution thread, which means that while your code is executing the browser will not be redrawing the page.
What you need to do is use the setTimeout(). Using these, there will be "spaces" in between your code execution in which the browser will get a chance to redraw the page.
UPDATED
You dont need to use FileReader, you can get the file from "onChange" on file selector.
added "streamFiles: true" into generateAsync options.
So, try this (It would be nice if your file is at least 8MB to clearly see the percentage):
var statusEl = document.getElementById("status");
const fileSelector = document.getElementById("file-selector");
fileSelector.onchange = function() {
var zip = new JSZip();
zip.file("myfile.txt", this.files[0]);
statusEl.innerHTML = "Compressing file";
zip.generateAsync({
type: "blob",
compression: "DEFLATE",
streamFiles: true,
compressionOptions: {
level: 6
}
},
updateCallback
)
.then(function(content) {
saveAs(content, "example.zip");
});
};
function updateCallback(metaData) {
const percent = metaData.percent;
setTimeout(function() {
//console.log(percent);
statusEl.textContent = percent.toFixed(2) + " %";
}, 10);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jszip/3.5.0/jszip.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/FileSaver.js/2.0.2/FileSaver.min.js"></script>
<label for="avatar">Choose a file:</label>
<input type="file"
id="file-selector" name="file-selector" accept=".txt">
<div id="status"></div>
I'm afraid you need a Worker.
var statusEl = document.getElementById("status");
const fileSelector = document.getElementById("file-selector");
fileSelector.addEventListener("change", (event) => {
var blob = new Blob([
document.querySelector('#worker1').textContent
], { type: "text/javascript" })
var worker = new Worker(window.URL.createObjectURL(blob));
worker.onmessage = function(e) {
statusEl.innerHTML = e.data;
console.log(e.data)
}
worker.postMessage(event.target.files[0]);
});
function readFile(event) {
statusEl.innerHTML = "Loading file";
const fileList = event.target.files;
console.log("qui", fileList[0]);
const reader = new FileReader();
reader.addEventListener("load", (event) => {
statusEl.innerHTML = "Compressing file";
console.log(event.target.result instanceof ArrayBuffer)
zipFile(event.target.result);
});
reader.readAsArrayBuffer(fileList[0]);
}
function zipFile(result) {
var zip = new JSZip();
zip.file("myfile.txt", result);
zip
.generateAsync(
{
type: "blob",
compression: "DEFLATE",
compressionOptions: {
level: 6
}
},
updateCallback
)
.then(function (content) {
saveAs(content, "example.zip");
});
}
function updateCallback(metaData) {
statusEl.innerHTML = metaData.percent.toFixed(2) + " %";
console.log(metaData.percent.toFixed(2) + " %");
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/FileSaver.js/2.0.2/FileSaver.min.js"></script>
<label for="avatar">Choose a file:</label>
<input type="file"
id="file-selector" name="file-selector">
<div id="status"></div>
<script id="worker1" type="javascript/worker">
importScripts("https://cdnjs.cloudflare.com/ajax/libs/jszip/3.5.0/jszip.min.js");
function readFile(file) {
const reader = new FileReader();
reader.addEventListener("load", (event) => {
zipFile(event.target.result);
});
reader.readAsArrayBuffer(file);
}
function zipFile(result) {
var zip = new JSZip();
zip.file("myfile.txt", result);
zip
.generateAsync(
{
type: "blob",
compression: "DEFLATE",
compressionOptions: {
level: 6
}
},
metaData => self.postMessage(metaData.percent.toFixed(2) + " %")
)
.then(function (content) {
saveAs(content, "example.zip");
});
}
self.onmessage = function(e) {
readFile(e.data);
};
</script>
I tested it with a 70MB file and more or less it works. There is still an initial delay probably related to the read file time, if you find a solution to pipe the content from FileReader to JSZip...

Multiple file upload using Laravel & Vue JS

so I've been trying to upload multiple image file using Vue JS with Laravel at server side.
My template vue
<input type="file" id = "file" ref="file" v-on:change="onImageChange" multiple />
My Javascript code
<script>
export default {
data(){
return{
product: {},
image: '',
}
},
created() {
let uri = `/api/product/edit/${this.$route.params.id}`;
this.axios.get(uri).then((response) => {
this.product = response.data;
});
},
methods:{
onImageChange(e){
let files = e.target.files || e.dataTransfer.files;
if (!files.length)
return;
this.createImage(files[0]);
},
createImage(file){
let reader = new FileReader();
let vm = this;
reader.onload = (e) => {
vm.image = e.target.result;
};
reader.readAsDataURL(file);
},
replaceByDefault(e) {
e.target.src = this.src='/uploads/products/default_image.jpg';
},
saveImage(e){
e.preventDefault()
var file = document.getElementById('file').files;
let formData = new FormData;
formData.append('productId', this.product.id)
formData.append('file', file[0])
axios.post('/api/product/image/add', formData, {
headers: {'Content-Type': 'multipart/form-data'}
}).then((response) => {
this.$router.push({name: 'view',params: { id: this.product.id }});
});
}
}
}
</script>
I saw somewhere the internet that in vue you can use looping the formData.append but how do i catch the data in the server side. Here is my ProductController
$saveImage = new Gallery;
$saveImage->product_id = $request->productId;
$file = request()->file('file');
$file_name = time().$file->getClientOriginalName();
$path = $imgUpload = Image::make($file)->save(public_path('/uploads/products/' . $file_name));
$saveImage->path = '/uploads/products/'.$file_name;
$saveImage->status = 1;
$saveImage->save();
return "success";
Thank you very much guys!
you can use request()->file('file') to get files. but you have to add some changes in your vue source when you are trying to send an array of files.
Vue
let formData = new FormData;
formData.append('productId', this.product.id)
// append files
for(let i=0; i<file.length; i++){
formData.append('file[]', file[i])
}
useing file[] instead of file will generate an array of files in request payload.
then in laravel side of code you can use request()->file('file') to get that array of files. but if you want just one of them (for example: first one) you can use request()->file('file.0') to get that file.

How to call a function inside a vue npm package?

What I'm trying to do is to display and modify the images that the car has "in my case", so I used the vue-upload-multiple-image package to save the images and went well, but when I call back these images to the same package I got stuck.
I convert the images that has been stored to base64 now what I want is the list of images go to specific function inside that package, so it will display the images when I try to update the car.
This is the function I want to call:
createImage(file) {
let reader = new FileReader()
let formData = new FormData()
formData.append('file', file)
reader.onload = e => {
let dataURI = e.target.result
if (dataURI) {
if (!this.images.length) {
this.images.push({
name: file.name,
path: dataURI,
highlight: 1,
default: 1,
})
this.currentIndexImage = 0
} else {
this.images.push({
name: file.name,
path: dataURI,
highlight: 0,
default: 0,
})
}
this.$emit(
'upload-success',
formData,
this.images.length - 1,
this.images,
)
}
}
reader.readAsDataURL(file)
},
The Function inside this file
I tried to console.log the function normally it outputs undefined,
I think of props but how it gonna help me.
mounted(){
console.log(this.createImage);
What I want is just to call this function inside my editcar component and sent to it the converter images.
Thank you for helping me and read the this far.
I Found the solution of the problem: in the doc there is dataImages prop. I use it like this:
<div class="form-group m-form__group">
<vue-upload-multiple-image
#upload-success="uploadImageSuccess"
#before-remove="beforeRemove"
#edit-image="editImage"
:dataImages="images"
></vue-upload-multiple-image>
</div>
And it must the images base64 so here is the function the the data.
data() {
return {
images :[],
}
},
mounted() {
this.ConvertImages();
},
This is methods:
methods: {
ConvertImages() {
let images = this.car.images
let image = this.images
for (var i = 0; i < images.length; i++) {
this.toDataURL(images[i].path, function(dataURL) {
image.push({
path: dataURL,
})
})
}
},
toDataURL(url, callback) {
var xhr = new XMLHttpRequest()
xhr.onload = function() {
var reader = new FileReader()
reader.onloadend = function() {
callback(reader.result)
}
reader.readAsDataURL(xhr.response)
}
xhr.open('GET', url)
xhr.responseType = 'blob'
xhr.send()
},
}, //END OF METHODS

Categories