I am using Vue.js in the front-end. I have Node.js, Express, PostgreSQL (with Sequelize ) on the backend.
I am storing an item in the database that includes a thumbnail image.
Database Model
const Item = sequelize.define('item', {
id: {
type: Sequelize.INTEGER,
primaryKey: true,
autoIncrement: true
},
name: {
type: Sequelize.TEXT,
allowNull: false,
},
image: {
type: Sequelize.BLOB('long'),
allowNull: true,
},
Database-wise, the image is being stored as a Blob and I think this is fine (and yes, I am aware that it's not best-practice to put images in a database).
I observe in the browser that the object that I am accessing in my Vue template with this.item.image is an Object of the type Buffer.
Adding to Database
I add the item to the database in the browser with this in my vue template:
<label for="image" class="itemCreate__field itemCreate__field--image">
<span class="itemCreate__fieldLabel">Image</span>
<input id="file" type="file" accept="image/*" #change="onFileChange"/>
<img v-if="itemPreviewImage" :src="itemPreviewImage" />
</label>
And that HTML relies on these methods:
onFileChange(evt) {
const files = evt.target.files || evt.dataTransfer.files;
if (!files.length) return;
this.createImage(files[0]);
},
createImage(file) {
const image = new Image();
const reader = new FileReader();
reader.onload = evt => {
this.itemPreviewImage = evt.target.result;
this.item.image = evt.target.result;
}
reader.readAsDataURL(file);
},
I have this in the vue template that renders the image:
<div v-if="item.image">
<img :src="imgUrl" alt="Picture of item"/>
</div>
Rendering from Database
I have tried the following approaches, which do not work:
createObjectUrl borrowed from here:
imgUrl(){
const objUrl = window.URL.createObjectURL(new Blob(this.item.image.data));
return objUrl;
}
Creating a base64 string borrowed from here:
imgUrl(){
const intArray = new Uint8Array(this.item.image.data);
const reducedArray = intArray.reduce((data, byte) => data + String.fromCharCode(byte), '');
const base64String = `data:image/png;base64, ${btoa(reducedArray)}`;
return base64String;
}
Creating a new Uint8Array and then getting an objectUrl (borrowed here):
imgUrl(){
const arrayBuffer = new Uint8Array(this.item.image);
const blob = new Blob([arrayBuffer], {type: "image/png"});
return window.URL.createObjectURL(blob);
}
In all cases (including some attempts with FileReader), I get broken images. I don't get errors in the console, though.
I think the issue is that I am not submitting correct data to the database.
I am sending an Ajax request that has the File attached as a property, and I should probably convert it to ¿something else?
First, be sure you're getting a valid base64 string: https://codebeautify.org/base64-to-image-converter
Then try defining a getter to the Item model
const Item = sequelize.define('item', {
...
image: {
type: Sequelize.BLOB('long'),
allowNull: true,
get () { // define a getter
const data = this.getDataValue('image')
return data ? data.toString('base64') : ''
},
set(val) {
this.setDataValue('image', val);
}
},
...
}
Computed property
imgURL () {
return this.item.image
? 'data:image/png;charset=utf-8;base64,' + this.item.image
: '' // some default image
}
Related
I'm using videojs in a vue application to play a pre-signed URL for a mp4 video file on S3. I'm using the pre-signed URL as the source of the videojs player but I get the error
The media could not be loaded, either because the server or network
failed or because the format is not supported.
This is how my URL looks like:
https://bucket-name.s3.region.amazonaws.com/object.mp4?AWSAccessKeyId=xxxxxxxxxxxx&Expires=xxxxxxx&Signature=xxxxxx&x-amz-security-token=xxxxxxx
I looked at similar questions on SO and someone suggested to change the URL format to below format, but that didn't work either.
https://s3.amazonaws.com/bucket-name/object.mp4?AWSAccessKeyId=xxxxxxxxxxxx&Expires=xxxxxxx&Signature=xxxxxx&x-amz-security-token=xxxxxxx
The video player plays the video if I put either of the above hardcoded URLs as its source, but not when I do as variables.
Why is it changing behaviour when a variable is used ?
<template>
<div>
<video-player
:options="{
autoplay: false,
controls: true,
sources: [
{
src: `${URL}`,
type: 'video/mp4',
},
],
}"
/>
</div>
</template>
<script>
import VideoPlayer from '#/components/VideoPlayer.vue';
var AWS = require('aws-sdk');
var bucketName = 'xxxx';
var s3 = new AWS.S3({
apiVersion: '2006-03-01',
params: { Bucket: bucketName },
});
export default {
components: {
VideoPlayer,
},
data() {
return {
URL: String,
};
},
methods: {
getURL() {
var self = this;
let myPromise = new Promise(function (myResolve, myReject) {
const url = s3.getSignedUrl('getObject', {
Key: `xxxxx.mp4`,
Expires: 3600,
});
myResolve(url);
myReject('sorry, error');
});
myPromise.then(
function (value) {
self.URL = value;
},
function (error) {
console.log(error);
}
);
},
},
mounted() {
this.getURL();
},
};
</script>
The issue was that the URL value was not being set as the source, as mentioned by #misterben.
I made the following changes to set the source in the method. Although there might be better ways of setting the source in Vue ( other than using querySelector)
<video-player
:options="{
autoplay: false,
controls: true,
}"
/>
</div>
and,
import videojs from 'video.js';
getSignedUrl() {
// this is because the imported videoPlayer component has class="video-js"
var player = videojs(document.querySelector('.video-js'));
var self = this;
var params = {
Bucket: 'xxxxx',
Key: `xxxxx`,
};
var promise = s3.getSignedUrlPromise('getObject', params);
promise.then(
async function (url) {
self.URL = url;
await player.src({
src: url,
type: 'video/mp4' /*video type*/,
});
},
function (err) {
console.log(err);
}
);
},
I am using html-pdf node module to generate a pdf and email via nodemailer. Everything is working file. Only One problem I am facing right now is that I can't set the name of my pdf file generated. I am attaching the code for generate pdf function.
const generatePdf = (data, user, startDate, endDate, linkRes) => {
return new Promise((res, rej) => {
console.log("*&&&&& Here in generate Report", linkRes);
const body = renderToString( <App meetingsData={data} userData={user} startDate={startDate} endDate={endDate} linkRes={linkRes} /> );
const title = 'Therify Meeting Report';
let returnObj = {};
const phantomPath = require('witch')('phantomjs-prebuilt', 'phantomjs');
const html = Html({body, title});
console.log("*&&&&& path", phantomPath);
const options = {
format: 'A4',
orientation: 'portrait',
// phantomPath: `${phantomPath}`,
header: {
"height": "3mm",
},
footer: {
"height": "5mm",
"contents": {
default: '<span style="color: #444;">{{page}}</span>/<span>{{pages}}</span>',
}
},
};
pdf.create(html, options).toBuffer(function (err, file) {
console.log('This is a file:', file);
if(err) {
returnObj.data = `Pdf Generation Failed`;
returnObj.status = 500;
res(returnObj);
}else {
buffer = file;
returnObj.data = `Pdf Generated Successfully`;
returnObj.status = 200;
res(returnObj);
}
});
});
}
The name of the pdf file generated is set to 'attachment-1.pdf'. I want to change it. Here is a picture attached of the pdf generated.
Use Like this
keep the below code inside the pdf.create function hope it will work
res.setHeader("Content-Disposition","attachment;Abc.pdf");
I have an Spring Boot endpoint, which expects a MultipartFile, to upload a profile picture:
#RequestMapping(value="/picture", method=RequestMethod.POST)
public ResponseEntity<Void> uploadProfilePicture(#RequestParam(name="file") MultipartFile file) {
URI uri = service.uploadProfilePicture(file);
return ResponseEntity.created(uri).build();
}
It's working fine on Postman. Just to mention, I don't specify any Content-Type header, and I choose the file normally in the Body->form-data Postman section. Works perfectly!
But now I'm trying to request that endpoint through Ionic. First of all, I'm taking a picture using a similar code from official documentation, which stores the picture as base64:
getCameraPicture() {
const options: CameraOptions = {
quality: 100,
destinationType: this.camera.DestinationType.DATA_URL,
encodingType: this.camera.EncodingType.JPEG,
mediaType: this.camera.MediaType.PICTURE
}
this.camera.getPicture(options)
.then(imageData => {
this.picture = 'data:image/jpeg;base64,' + imageData;
},
error => {
});
}
The above Ionic code is working fine: the picture taken is being stored in my picture variable and I could also see it in my screen using a <img [scr]="picture"> element.
Now I want to send that picture to my endpoint, but I'm not sure how to do that. The basic idea is to call an uploadPicture method from a service:
sendPicture() {
this.clientService.uploadPicture(this.picture)
.subscribe(response => {
...
},
error => {
...
});
}
And then implement that method in the service class:
uploadPicture(picture) {
let formData: FormData = new FormData();
formData.append('file', picture);
return this.http.post(
`${API_CONFIG.baseUrl}/clientes/picture`,
formData,
{
observe: 'response',
responseType: 'text'
}
);
}
I'm not sure if it's necessary to convert that base64 picture to blob or whatever. Anyway, I'm getting a MissingServletRequestPartException from my backend: "Required request part 'file' is not present".
So how to upload a base64 image to a Spring Boot endpoint which expects a MultipartFile?
You need to turn your base64 data to blob then you should send it.
dataURItoBlob(dataURI) {
var byteString = atob(dataURI.split(',')[1]);
var mimeString = dataURI.split(',')[0].split(':')[1].split(';')[0]
var ab = new ArrayBuffer(byteString.length);
var ia = new Uint8Array(ab);
for (var i = 0; i < byteString.length; i++) {
ia[i] = byteString.charCodeAt(i);
}
var blob = new Blob([ab], {type: mimeString});
return blob;
}
reference
I am trying to create a simple editor for writing a storyline. Right now I could show the html to markup in the editor where bold text are shown in bold and etc. I could also send the data in html form to server but I could not show the image in an editor and also could not upload the image in editor. I have created a codesandbox of it. Here is the link
https://codesandbox.io/s/5w4rp50qkp
The line of code is a bit huge. That is why I am posting the code in codesandbox where you can see the demo either.
Can anyone please help me to make this possible?
I originally posted my answer here
I copied it below for your convenience:
Took a while to figure out how to insert image after checking Draft.js source code.
insertImage = (editorState, base64) => {
const contentState = editorState.getCurrentContent();
const contentStateWithEntity = contentState.createEntity(
'image',
'IMMUTABLE',
{ src: base64 },
);
const entityKey = contentStateWithEntity.getLastCreatedEntityKey();
const newEditorState = EditorState.set(
editorState,
{ currentContent: contentStateWithEntity },
);
return AtomicBlockUtils.insertAtomicBlock(newEditorState, entityKey, ' ');
};
Then you can use
const base64 = 'aValidBase64String';
const newEditorState = this.insertImage(this.state.editorState, base64);
this.setState({ editorState: newEditorState });
For render image, you can use Draft.js image plugin.
Live demo: codesandbox
The demo inserts a Twitter logo image.
If you want to insert a image from local file, you can try to use FileReader API to get that base64.
For how to get base64, it is simple, check
Live demo: jsbin
Now go ahead to put them together, you can upload image from local file!
Set uploadEnable to true
and call uploadCallBack
<Editor ref='textEditor'
editorState={this.state.editorState}
toolbar={{
options: ['image',],
image: {
uploadEnabled: true,
uploadCallback: this.uploadCallback,
previewImage: true,
inputAccept: 'image/gif,image/jpeg,image/jpg,image/png,image/svg',
alt: { present: false, mandatory: false },
defaultSize: {
height: 'auto',
width: 'auto',
},
},
}}
onEditorStateChange={this.onEditorStateChange}
/>
then define your uploadCallback if you want to read file directly from local
uploadCallback = (file) => {
return new Promise(
(resolve, reject) => {
if (file) {
let reader = new FileReader();
reader.onload = (e) => {
resolve({ data: { link: e.target.result } })
};
reader.readAsDataURL(file);
}
}
);
}
Or if you want to use a file server to keep files then you might want to upload image on server and then simply put the link
uploadCallback = (file) => {
return new Promise((resolve, reject) => {
const data = new FormData();
data.append("storyImage", file)
axios.post(Upload file API call, data).then(responseImage => {
resolve({ data: { link: PATH TO IMAGE ON SERVER } });
})
});
}
Am working on a web application and we allow users to upload files to our server. Am trying to do client side compression before uploading files to the server. What would be the better way to achieve this using HTML5 and JavaScript.
Thanks.
The common mechanism to do what you want is using FileReader and a JavaScript client-side compression library (i.e. compressjs).
In 2022 it's almost too simple, if the browser supports CompressionStream, FormData and Response.
In the example below I use FormData to collect all the fields from the form.
Then I use the readable stream from the file, and pipe it though the compression stream. Then I use Response to read everything from the compressed stream and return it in a blob.
async function compress(file, encoding = 'gzip') {
try {
return {
data: await new Response(file.stream().pipeThrough(new CompressionStream(encoding)), {
headers: {
'Content-Type': file.type
},
}).blob(),
encoding,
};
} catch (error) {
// If error, return the file uncompressed
console.error(error.message);
return {
data: file,
encoding: null
};
}
}
theForm.addEventListener(
'submit',
(event) => event.preventDefault()
)
theForm.addEventListener(
'input',
async function(event) {
// collect all fields
const fd = new FormData(theForm);
// Get 'file handle' from imput elemen
const file = fd.get('theFile');
if (!file) return
const encoding = fd.get('theEncoding');
const compressed = await compress(file, encoding);
theMessage.value = [
'Compressed with', compressed.encoding,
'Source file was', file.size, 'bytes',
'and the compressed file', compressed.data.size,
'saving', ((1 - compressed.data.size / file.size) * 100)
.toFixed(0),
'%.'
].join(' ')
}
)
form>* {
display: block;
width: 100%;
}
<form id="theForm">
<select name="theEncoding">
<option>gzip</option>
<option>deflate</option>
<option>deflate-raw</option>
</select>
<input type="file" name="theFile" id="theFile">
</form>
<output id="theMessage"></output>