Fileupload - Get image resolution - vue.js

I am trying to get the resolution (height, width) of the images I am sending to the backend. Attention, in this example several images are sent to the backend at once. So, we are working with loops!
I tried to find a solution in the backend, but couldn't find anything. Here is the corresponding stackoverflow question I asked.
Now I am asking for a way to do this in the frontend. When I console.log(file) following example will be outputed:
lastModified: 1657671728196
lastModifiedDate: Wed Jul 13 2022 02:22:08 GMT+0200 (Mitteleuropäische Sommerzeit) {}
name: "Example.png"
size: 3128
type: "image/png"
webkitRelativePath: ""
Too bad that the resolution is not callable this way. Furthermore I would like to save the img-type in the db but not as image/png but as png or jpeg etc. only.
async submitFiles() {
const formData = new FormData()
for (let i = 0; i < this.files.length; i++) {
const file = this.files[i]
formData.append('files', file)
console.log(file)
}
try {
const response = await this.$axios.post('/api/upload-files', formData, {
headers: { 'Content-Type': 'multipart/form-data' },
onUploadProgress: (progressEvent) => {
this.uploadPercentage = parseInt(
Math.round((progressEvent.loaded / progressEvent.total) * 100)
)
},
})
const result = response.data
if (result.success) {
if (this.uploadPercentage === 100) {
this.$nuxt.refresh()
this.$emit('exit', true)
}
}
} catch (err) {
console.log(err)
}
},

Checking for resolution in the browser is pretty straightforward. All you need to do is create an Image element out of the user provided file.
export default {
data() {
return {
files: [],
formData: null
}
},
watch: {
formData() {
if (this.formData.getAll('files').length === this.files.length) {
this.submitFiles()
}
},
},
mounted() {
this.formData = new FormData()
},
methods: {
async submitFiles() {
try {
const data = this.formData
const response = await this.$axios.post(
'/api/upload-files', data,
{
headers: { 'Content-Type': 'multipart/form-data' },
onUploadProgress: (progressEvent) => {
this.uploadPercentage = parseInt(
Math.round((progressEvent.loaded / progressEvent.total) * 100)
)
},
}
)
const result = response.data
if (result.success) {
if (this.uploadPercentage === 100) {
this.$nuxt.refresh()
this.$emit('exit', true)
}
}
} catch (err) {
console.log(err)
}
},
processFiles() {
for (let i = 0; i < this.files.length; i++) {
const file = this.files[i]
const img = new Image()
const vm = this
img.onload = function () {
file.width = this.width
file.height = this.height
console.log(file)
vm.formData.append('files', file)
}
img.src = URL.createObjectURL(file)
}
},
},
}
As for the mime type of the image you could split the string by / and save the right half:
const [,type] = file.type.split('/')
EDIT:
The callbacks will not be called when you try to post your data (inside the try block), so it would be a good idea to watch the formData until it's length matches the length of the files array. Then we know all of the images are processed and it's safe to send data to our backend.

Related

VueJS Axios onUploadProgress inside the component from service

I am trying to create a progress bar in vuejs ... however, all the tutorials are calling directly axios inside the component... my setup is a bit more ... different :)
what I have in component is this upload method
uploadPhotos() {
const formData = new FormData();
this.gallery_photos.forEach(photo => {
formData.append(`photo_${photo.name}`, photo);
});
PhotoService.uploadPhotos(this.gallery.id_gallery, formData, callback => {
console.log(callback);
})
.then(() => {
console.log("OK");
})
.catch(error => {
console.log(error);
});
}
and this is my service
uploadPhotos(id, photos) {
const config = {
onUploadProgress: function (progressEvent) {
const percentCompleted = Math.round((progressEvent.loaded * 100) / progressEvent.total);
console.log(percentCompleted);
}
};
return axios.post(`galleries/${id}/photos`, photos, { headers: authHeader() }, config);
}
However, from neither of those I am getting the input ... what am I missing? :(
Everything else is working fine... I am getting the files on the server side so I can process them correctly.. I just have no idea what the onUploadProgress is not doing anything
Might need to combine the headers into the config
uploadPhotos(id, photos) {
const config = {
onUploadProgress: function (progressEvent) {
const percentCompleted = Math.round((progressEvent.loaded * 100) / progressEvent.total);
console.log(percentCompleted);
},
headers: authHeader()
};
return axios.post(`galleries/${id}/photos`, photos, config);
}
You need to add a callback parameter to your function and trigger it:
uploadPhotos(id, photos, cb) {
const config = {
onUploadProgress: function (progressEvent) {
const percentCompleted = Math.round(
(progressEvent.loaded * 100) / progressEvent.total
);
cb(percentCompleted); // <== you can pass here whatever you want
},
};
return axios.post(
`galleries/${id}/photos`,
photos,
{ headers: authHeader() },
config
);
}
Now you will see your data:
PhotoService.uploadPhotos(this.gallery.id_gallery, formData, percent => {
console.log(percent);
})

How to encode and parse / decode a nested query string Javascript

I'm sending form data from React Hook Form to Netlify via their submission-created function. I don't have any problem with encoding individual form field values, but now I'm trying to encode an array of objects.
Here is an example of my form data:
{
_id: "12345-67890-asdf-qwer",
language: "Spanish",
formId: "add-registration-form",
got-ya: "",
classType: "Private lessons",
size: "1",
days: [
{
day: "Monday",
start: 08:00",
end: "09:30"
},
{
day: "Wednesday",
start: "08:00",
end: "09:30"
}
]
}
The only problem I have is with the "days" array. I've tried various ways to encode this and this is the function I've currently been working with (which isn't ideal):
const encode = (data) => {
return Object.keys(data).map(key => {
let val = data[key]
if (val !== null && typeof val === 'object') val = encode(val)
return `${key}=${encodeURIComponent(`${val}`.replace(/\s/g, '_'))}`
}).join('&')
}
I tried using a library like qs to stringify the data, but I can't figure out how to make that work.
And here is the function posting the data to Netlify:
// Handles the post process to Netlify so I can access their serverless functions
const handlePost = (formData, event) => {
event.preventDefault()
fetch(`/`, {
method: "POST",
headers: { "Content-Type": "application/x-www-form-urlencoded" },
body: encode({ "form-name": 'add-registration-form', ...formData }),
})
.then((response) => {
if(response.status === 200) {
navigate("../../")
} else {
alert("ERROR!")
}
console.log(response)
})
.catch((error) => {
setFormStatus("error")
console.log(error)
})
}
Finally, here is a sample of my submission-created file to receive and parse the encoded data:
const sanityClient = require("#sanity/client")
const client = sanityClient({
projectId: process.env.GATSBY_SANITY_PROJECT_ID,
dataset: process.env.GATSBY_SANITY_DATASET,
token: process.env.SANITY_FORM_SUBMIT_TOKEN,
useCDN: false,
})
const { nanoid } = require('nanoid');
exports.handler = async function (event, context, callback) {
// Pulling out the payload from the body
const { payload } = JSON.parse(event.body)
// Checking which form has been submitted
const isAddRegistrationForm = payload.data.formId === "add-registration-form"
// Build the document JSON and submit it to SANITY
if (isAddRegistrationForm) {
// How do I decode the "days" data from payload?
let schedule = payload.data.days.map(d => (
{
_key: nanoid(),
_type: "classDayTime",
day: d.day,
time: {
_type: "timeRange",
start: d.start,
end: d.end
}
}
))
const addRegistrationForm = {
_type: "addRegistrationForm",
_studentId: payload.data._id,
classType: payload.data.classType,
schedule: schedule,
language: payload.data.language,
classSize: payload.data.size,
}
const result = await client.create(addRegistrationForm).catch((err) => console.log(err))
}
callback(null, {
statusCode: 200,
})
}
So, how do I properly encode my form data with a nested array of objects before sending it to Netlify? And then in the Netlify function how do I parse / decode that data to be able to submit it to Sanity?
So, the qs library proved to be my savior after all. I just wasn't implementing it correctly before. So, with the same form data structure, just make sure to import qs to your form component file:
import qs from 'qs'
and then make your encode function nice and succinct with:
// Transforms the form data from the React Hook Form output to a format Netlify can read
const encode = (data) => {
return qs.stringify(data)
}
Next, use this encode function in your handle submit function for the form:
// Handles the post process to Netlify so we can access their serverless functions
const handlePost = (formData, event) => {
event.preventDefault()
fetch(`/`, {
method: "POST",
headers: { "Content-Type": "application/x-www-form-urlencoded" },
body: encode({ "form-name": 'add-registration-form', ...formData }),
})
.then((response) => {
reset()
if(response.status === 200) {
alert("SUCCESS!")
} else {
alert("ERROR!")
}
console.log(response)
})
.catch((error) => {
console.log(error)
})
}
Finally, this is what your Netlify submission-created.js file should look like more or less:
const sanityClient = require("#sanity/client")
const client = sanityClient({
projectId: process.env.GATSBY_SANITY_PROJECT_ID,
dataset: process.env.GATSBY_SANITY_DATASET,
token: process.env.SANITY_FORM_SUBMIT_TOKEN,
useCDN: false,
})
const qs = require('qs')
const { nanoid } = require('nanoid');
exports.handler = async function (event, context, callback) {
// Pulling out the payload from the body
const { payload } = JSON.parse(event.body)
// Checking which form has been submitted
const isAddRegistrationForm = payload.data.formId === "add-registration-form"
// Build the document JSON and submit it to SANITY
if (isAddRegistrationForm) {
const parsedData = qs.parse(payload.data)
let schedule = parsedData.days
.map(d => (
{
_key: nanoid(),
_type: "classDayTime",
day: d.day,
time: {
_type: "timeRange",
start: d.start,
end: d.end
}
}
))
const addRegistrationForm = {
_type: "addRegistrationForm",
submitDate: new Date().toISOString(),
_studentId: parsedData._id,
classType: parsedData.classType,
schedule: schedule,
language: parsedData.language,
classSize: parsedData.size,
}
const result = await client.create(addRegistrationForm).catch((err) => console.log(err))
}
callback(null, {
statusCode: 200,
})
}

Make a get request until a response comes from a request post vue js

I should upload file in my vue.js app. When I browse a file, I make post request and until I get a response, I need to make get request every 2 seconds for example. How can I do this?
uploadFile(event) {
this.isLoadingProcess = true;
console.log(this.isLoadingProcess);
let data = new FormData();
this.file = event.target.files[0];
data.append('name', 'uploaded-file');
data.append('file', event.target.files[0]);
const options = {
headers: {
'Content-Type': event.target.files[0].type,
},
onUploadProgress: function (progressEvent) {
const {loaded, total} = progressEvent;
let percentCompleted = Math.floor((loaded * 100) / total);
//console.log(percentCompleted);
if (percentCompleted !== 100) {
axios.get(url, {data: progressId})
.then(response => {
this.progress = response.data.progress;
// console.log(this.progress);
console.log(response.data.progress);
})
}
},
};
axios.post(
url,
data,
options
)
.then(response => {
console.log(response);
})
.finally(()=> {
this.isLoadingProcess = false;
})
what about this?
It asks api about update more than every 2 seconds, but It's better for my opinion in case, when endpoint isn't available
var processTimeoutFunction = null;
function checkProgress() {
axios.get('ckeck-process', ...).then(response => {
if (response.processIsActive) {
processTimeoutFunction = setTimeout(() => checkProgress(), 2000);
} else {
clearTimeout(processTimeoutFunction);
this.isLoadingProcess = false;
})
}

Hapi Js File Upload Struggling

today i trying to get some file upload with Hapi Js, i follow all Google Result with similarity of code.
this the code :
server.route({
method: "POST",
path: `${PUBLIC_URL}${THEME_URL}/create`,
handler: async (request: any, reply: ResponseToolkit) => {
console.log(request.payload.file, 'payload')
return reply.response(request.payload)
},
options: {
payload: {
output: 'stream',
allow: 'multipart/form-data',
parse: false,
}
}
})
with thats code i cant get request.payload my file or data, this is my request with postman:
post file with postman
enter image description here
i got undifined at request.payload.file
if i turn payload :{parse:true} i get unsuported media types
thanks for attention
If you are using the below version then you must be using the following syntax
#hapi/hapi: 18.x.x +
payload: {
parse: true,
multipart: {
output: 'stream'
},
maxBytes: 1000 * 1000 * 5, // 5 Mb
}
Also, you can also try using Joi to validate your payload.
{
method: 'POST',
path: '/upload',
options: {
payload: {
maxBytes: 209715200,
output: 'stream',
parse: true,
allow: 'multipart/form-data',
multipart: true // <-- this fixed the media type error
},
handler: async (req, reply) => {
try {
// await importData(req.payload)
// return reply.response("IMPORT SUCCESSFULLY")
const data = await req.payload;
// let final = await importFile(data)
// return reply.response("final", final)
if (data.file) {
let name = await data.file.hapi.filename;
console.log("FIlename: " + name);
let path = await __dirname + "/uploads/" + name;
let file = await fs.createWriteStream(path);
await data.file.pipe(file);
await data.file.on('end', async function (err) {
// var ret = {
// filename: data.file.hapi.filename,
// headers: data.file.hapi.headers
// }
if (typeof require !== 'undefined')
XLSX = require('xlsx');
const workbook = await XLSX.readFile(path);
var sheetName = workbook.SheetNames;
console.log("row======>>>>");
await sheetName.forEach(async () => {
let xlData = await XLSX.utils.sheet_to_json(workbook.Sheets[sheetName[0]]);
console.log("xlData", xlData);
for (let i = 0; i < xlData.length; i++) {
console.log("if condition", xlData[i].phone)
const userCheck = await getUserIdService({ where: { phone: xlData[i].phone } });
console.log("userCheck", userCheck.data)
console.log("test", !(userCheck.data === null));
if (!(userCheck.data === null)) {
console.log("finally ", userCheck.data?.phone)
await uploadUpdateService(xlData[i], { where: { phone: userCheck.data?.phone } });
// return finalUpdate
// return reply.response("updated")
}
else if (!xlData[i].customerID) {
await uploadCreate(xlData[i]);
// return finalCreate
}
}
})
})
}
} catch (err) {
console.log('Err----------------------' + err);
// error handling
return reply.response(Boom.badRequest(err.message, err))
// return reply.response(Boom.badRequest(err.message, err));
}
}
}
}

How to place a component's logic outside the file .vue itself

I'm building a webapp in Nuxt.js and it's growing quite a bit.
I have a page which does two things: one when i'm creating a task and one when managing that task.
This page has a lot of methods, divided for when i create the task and when i manage the task.
How can i split these modules in two files and then import then only when I need them?
These methods need also to access the component's state and other function Nuxt imports such as axios.
async create() {
if (this.isSondaggioReady()) {
try {
await this.getImagesPath()
const o = { ...this.sondaggio }
delete o.id
o.questions = o.questions.map((question) => {
delete question.garbageCollector
if (question.type !== 'checkbox' && question.type !== 'radio') {
delete question.answers
delete question.hasAltro
} else {
question.answers = question.answers.map((answer) => {
delete answer._id
delete answer.file
delete answer.error
if (answer.type !== 'image') delete answer.caption
return answer
})
}
if (question.hasAltro) {
question.answers.push({
type: 'altro',
value: ''
})
}
return question
})
console.log('TO SEND', JSON.stringify(o, null, 2))
this.$store.commit('temp/showBottomLoader', {
show: true,
message: 'Crezione del sondaggio in corso'
})
const { data } = await this.$axios.post('/sondaggi/admin/create', o)
this.sondaggio.id = data
const s = {
_id: data,
author: this.$auth.user.email.slice(0, -13),
title: this.sondaggio.title
}
this.$store.commit('temp/pushHome', { key: 'sondaggi', attr: 'data', data: [...this.$store.state.temp.home.sondaggi.data, s] })
this.$store.dispatch('temp/showToast', 'Sondaggio creato correttamente')
this.$router.replace('/')
} catch (e) {
console.log(e)
this.$store.dispatch('temp/showToast', this.$getErrorMessage(e))
} finally {
this.$store.commit('temp/showBottomLoader', {
show: false,
message: 'Crezione del sondaggio in corso'
})
}
}
},
Here there's an example of what a method does. It calls an async functions which relies on HTTP axios requests:
async getImagesPath() {
this.sondaggio.questions.forEach((question, i) => {
question.answers.forEach((answer, j) => {
if (answer.file instanceof File || answer.value.includes('data:image')) {
this.uploadingImages.push({
coords: [i, j],
percentage: 0,
file: answer.file || answer.value
})
}
})
})
const requests = []
this.uploadingImages.forEach((img) => {
const temp = new FormData()
temp.append('img', img.file)
const req = this.$axios.post('/sondaggi/admin/images/add/' + this.sondaggio.title.replace(/\s+/g, ''), temp, {
onUploadProgress: function (progressEvent) {
img.percentage = Math.round(((progressEvent.loaded * 100) / progressEvent.total) * 90 / 100)
},
onDownloadProgress: function (progressEvent) {
img.percentage = 90 + Math.round(((progressEvent.loaded * 100) / progressEvent.total) * 10 / 100)
},
headers: { 'Content-Type': 'multipart/form-data' }
})
.catch((err) => {
console.log(err)
img.percentage = 150
})
requests.push(req)
})
try {
const response = await Promise.all(requests)
response.forEach(({ data }, i) => {
this.sondaggio.questions[this.uploadingImages[i].coords[0]].answers[this.uploadingImages[i].coords[1]].value = data[0]
})
this.$set(this.sondaggio, 'hasImages', this.uploadingImages.length > 0)
this.uploadingImages = []
await Promise.resolve()
} catch (e) {
console.log('handling gloval err', e)
await Promise.reject(e)
}
},
As you can see axios requests modify the component's state