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 } });
})
});
}
Related
The test objective is to confirm that an uploaded images 'src' attribute changes if a user uploads a new image, meaning the image has changed.
I've tried to use a couple approaches, outlined below.
First Approach
cy.get('.img').then($img => {
//store the src
const source = $('img').attr('src');
/**
* File drop 2mb jpg
*/
cy.fixture(Cypress.env("JPG_2MB_ASSET"), "base64").then(fileContent => {
cy.get(".dropzone").upload(
{
fileContent,
fileName: "sampleimage.jpg",
mimeType: "image/jpeg"
},
{ subjectType: "drag-n-drop" }
);
});
cy.wait(16000);
cy.get('img')
.attr('src')
.should($src2 => {
expect($src2).not.to.eq(source);
});
Second Approach
//store the src
const source = $img.attr('src')
/**
* File drop 2mb jpg
*/
cy.fixture(Cypress.env("JPG_2MB"), "base64").then(fileContent => {
cy.get(".dropzone").upload(
{
fileContent,
fileName: "sampleimage.jpg",
mimeType: "image/jpeg"
},
{ subjectType: "drag-n-drop" }
);
});
cy.wait(16000);
cy.get("img").attr('src').should(($src2) => {
expect($src2).not.to.eq(source)
Third Approach
cy.get("img")
.attr("src")
.then($src1 => {
/**
* File drop 2mb jpg
*/
cy.fixture(Cypress.env("JPG_2MB"), "base64").then(fileContent => {
cy.get(".dropzone").upload(
{
fileContent,
fileName: "sampleimage.jpg",
mimeType: "image/jpeg"
},
{ subjectType: "drag-n-drop" }
);
});
cy.wait(16000);
cy.get('img')
.attr('src')
.should($src2 => {
expect($src2).not.to.eq($src1);
});
The uploads work great, but the comparison of the src does not.
First & Second Approach
Expected- it stores the first image's src as the const source, and it drops a 2mb jpg. It then compares the 2nd image's src to the first and confirm they are different.
Result- ReferenceError: $ is not defined
Third Approach
Expected- It stores the first src as $src1, and then compares it to the second src, $src2
Result- cy.get(...).attr is not a function
After a plentitude of being angry at my keyboard, I have found the winning solution.
Using invoke you can store the value of an attr like src, as shown here:
https://docs.cypress.io/api/commands/invoke.html#Arguments-are-automatically-forwarded-to-the-function
So, after a few tweaks to the above attempts, I found this to work perfectly:
cy.get('.image-root img').invoke('attr', 'src').then((firstSrc) => {
const src1 = firstSrc
/**
* File drop 2mb jpg
*/
cy.fixture(Cypress.env("JPG_2MB"), "base64").then(fileContent => {
cy.get(".dropzone").upload(
{
fileContent,
fileName: "sampleimage.jpg",
mimeType: "image/jpeg"
},
{ subjectType: "drag-n-drop" }
);
});
cy.wait(16000);
cy.get('.image-root img').invoke('attr', 'src').then((nextSrc) => {
expect(nextSrc).to.not.equal(src1)
});
});
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 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
}
I am using Angular2. I am getting PDF response as BLOB from backend API. The PDF is showing fine in iframe but it is showing title as 'anonymous'. Can someone please guide?
html code:
<iframe id="showPDFIframe" allowtransparency="false" title="TestPDF" width="100%" height="800" [attr.src]="dataLocalUrl" type="application/pdf"></iframe>
pdf.component.ts
pdfDownload: any;
protected dataLocalUrl: SafeResourceUrl;
ngOnInit() {
this.requestOptions = this.createRequestOptions();
this.requestOptions.responseType = ResponseContentType.Blob;
this._pdfModelService.showPDF(this.requestOptions)
.subscribe( (res) => {
this.pdfDownload = res;
this.dataLocalUrl = this.domSanitizer.bypassSecurityTrustResourceUrl(window.URL.createObjectURL(res));
}, err => {
console.log(err);
})
}
pdfModelService.ts
showPDF(options?: RequestOptions): any {
return this._http.get(this.endpoints.showPDF.uri, options)
.map( (res) => {
return new Blob([res], { type: 'application/pdf' })
});
}
See below image 'Anonymous' is showing
Note: backend API gives the bytes which we cast in BLOB.
have you tried providing title in the options:
showPDF(options?: RequestOptions): any {
return this._http.get(this.endpoints.showPDF.uri, options)
.map( (res) => {
return new Blob([res], { type: 'application/pdf', title: 'testpdf' })
});
}
Although I am not certain why the specified title field "TestPDF" in the code is not appearing on the page, the "(anonymous)" value that is displaying could simply be pulling the meta data from the PDF file itself. A possible solution would be to check the title field in the PDF document properties to set the title there. In Adobe Acrobat, from the file menu select Properties > Description to check/update the title field.
Reference article from W3.org: https://www.w3.org/TR/WCAG20-TECHS/PDF18.html
I want to upload a file using the extjs6 modern toolkit. Therefor I display a MessageBox with a file chooser. How can I retrieve the selected file into a javascript object after clicking the OK button to upload it (via HTTP POST e.g.)?
this.createUploadMsgBox("File Upload", function (clickedButton) {
if (clickedButton == 'ok') {
console.log("file: " + file);
}
createUploadMsgBox: function (title, callback) {
Ext.Msg.show({
title: title,
width: 300,
buttons: Ext.MessageBox.OKCANCEL,
fn: callback,
items: [
{
xtype: 'filefield',
label: "File:",
name: 'file'
}
]
});
}
You can rum my example here:
https://fiddle.sencha.com/#view/editor&fiddle/1kro
You have two posible solutions.
One is to use a form, and send the file via form.submit() (use form.isValid() before the submit). You can retrieve the file in the server with a MultipartFile.
The other way is to use JS File API. In you createUploadMsgBox function:
this.createUploadMsgBox("File Upload", function (clickedButton) {
if (clickedButton == 'ok') {
//console.log("file: " + file);
var filefield = Ext.ComponentQuery.query('filefield')[0];
var file = filefield.el.down('input[type=file]').dom.files[0];
var reader = new FileReader();
reader.onload = (function(theFile) {
return function(e) {
console.log(e.target.result);
};
})(file);
reader.readAsBinaryString(file);
}
});
In the file object you have the basic info of the file, and then you will see in the console the content of the file.
Hope this helps!