I am trying to implement custom upload adapter but it is not working.
On uploading image my upload function is not running(upload function is created inside uploadAdapter function) and no request is getting sent to backend. Please see what is the problem in code.
const API_URl = "https://noteyard-backend.herokuapp.com";
const UPLOAD_ENDPOINT = "api/blogs/uploadImg";
function uploadAdapter(loader) {
return {
upload: () => {
return new Promise((resolve, reject) => {
const body = new FormData();
loader.file.then(file => {
body.append("uploadImg", file);
//AuthorizedApi is simple axios request.
AuthorizedApi.post(`${API_URl}/${UPLOAD_ENDPOINT}`, {
body,
})
.then(res => res.json())
.then(res => {
resolve({ default: `${API_URl}/${res.url}` });
})
.catch(err => {
reject(err);
});
});
});
},
};
}
function uploadPlugin(editor) {
editor.plugins._plugins.get("FileRepository").createUploadAdapter = loader => {
return uploadAdapter(loader);
};
}
//This is my CKEditor code snippet
<CKEditor
editor={TextEditor}
config={{
...EDITOR_CONFIG[theme],
extraPlugins: [uploadPlugin],
link: {
defaultProtocol: "https://",
},
}}
data={stripTrackingImage(value) ?? ""}
onReady={editor => {
//add toolbar
window.editor = editor;
if (disabled) {
editor.isReadOnly = true;
} else {
document
.querySelector(`#${toolbarId}`)
?.appendChild(editor.ui.view.toolbar.element);
editor.on("handle_attachment_click", addAttachment);
//adjust tooltip position
editor.ui.view.toolbar.items.map(item => {
//button positionging( without dropdowns)
item.tooltipPosition = "se";
//for dropdowns
if (item.buttonView) {
item.buttonView.tooltipPosition = "se";
}
return item;
});
}
console.log(editor, "editor");
}}
onChange={(event, editor) => setValue(editor.getData())}
isReadOnly={disabled}
/>
On uploading image , no request is getting sent to given URL, please try to find the problem
Related
I do not understand what's going on when it comes to creating an img dynamically. This is the code:
const pics = document.querySelector('.gridPics');
eventosListener();
function eventosListener() {
document.addEventListener('DOMContentLoaded', () => {
jsonFile();
});
}
I get the pics from a json file. It works well.
function jsonFile () {
fetch('js/fotos.json')
.then(response => {
if (!response.ok) {
throw new Error(response.status);
}
return response.json();
})
.then(data => {
showPics(data);
})
.catch(function () {
this.dataError = true;
})
}
Function that must create pictures dinamically
function showPics(x) {
x.forEach(element => {
const div = document.createElement('div');
div.classList.add('grid-item');
const imagen = document.createElement('img');
imagen.setAttribute("src", element.name);
imagen.setAttribute("alt", element.desc);
imagen.setAttribute("width", '90%');
div.appendChild(imagen);
pics.appendChild(div);
});
}
HTML file
<section class="fotos">
<div class="gridPics"></div>
</section>
For a while I believed that imgs paths were wrong, but I added a img like this but it does no work either
imagen.src = 'https://i.picsum.photos/id/861/200/300.jpg?hmac=kssNLkcAZTJQKpPCSrGodykV8A6CStZxL7dHvtsVUD0';
Json File
[{
"name" : "./../assets/1.jpeg",
"desc": "imagen1"
},...
}]
The estructure
enter image description here
I do not have any error in the console but I can not see anything.
I suspect your issue is that the data is not coming in as JSON as you're loading it from a plain text file, you can solve this by ensuring they are using JSON.parse.
The below works (ignoring me having to mock out loading the data) just adding showPics(JSON.parse(data));
const jsonData = "[{\"desc\":\"imagen1\",\"name\":\"https://i.picsum.photos/id/861/200/300.jpg?hmac=kssNLkcAZTJQKpPCSrGodykV8A6CStZxL7dHvtsVUD0\" }]";
const fetch = () => Promise.resolve({ok:true, json:() => Promise.resolve( jsonData)});
const pics = document.querySelector('.gridPics');
eventosListener();
function eventosListener() {
document.addEventListener('DOMContentLoaded', () => {
jsonFile();
});
}
function jsonFile () {
fetch('js/fotos.json')
.then(response => {
if (!response.ok) {
throw new Error(response.status);
}
return response.json();
})
.then(data => {
showPics(JSON.parse(data));
})
.catch(function () {
this.dataError = true;
})
}
function showPics(x) {
x.forEach(element => {
const div = document.createElement('div');
div.classList.add('grid-item');
const imagen = document.createElement('img');
imagen.setAttribute("src", element.name);
imagen.setAttribute("alt", element.desc);
imagen.setAttribute("width", '90%');
div.appendChild(imagen);
pics.appendChild(div);
});
}
<section class="fotos">
<div class="gridPics"></div>
</section>
I'm using Vue with Laravel and I tried to do a simple crud which is working good regarding things like title or description of an article (text-fields) but when I try to send an image file, it doesn't work. I have tried formData but to no avail.
This is my form in template, title goes in the database with no problem, if I console log selectedFile then it shows the file selected correctly but the addArticle method not attaching the images
<form #submit.prevent="addArticle" class="container">
<label>Title</label>
<input type="text" v-model="article.title" />
<label>Image</label>
<input type="file" v-on:change="selectedFile" />
<button type="submit">Create</button>
</form>
This is my script
<script>
export default {
data() {
return {
fileSelected: null,
article: {},
};
},
methods: {
addArticle() {
var formData =new FormData();
formData.append(this.article.image, this.fileSelected);
axios
.post("http://localhost:8000/api/articles", this.article)
.then((response) => this.$router.push({ name: "ArticlesList" }))
.catch((err) => console.log(err))
.finally(() => (this.loading = false));
}
,
selectedFile(event) {
this.fileSelected = event.target.files[0];
},
},
};
</script>
this is my code
<input type="file" #change="onFileChange">
onFileChange(e) {
this.sendphoto = e.target.files[0];
},
editUser() {
var self = this;
let formData = new FormData();
formData.append("image" , self.sendphoto )
let config = {
header : {
'Content-Type' : 'multipart/form-data'
}
}
axios.post('/edit-user' , formData , config)
.then(function (response) {
})
.catch(function (error) {
console.log(error);
})
},
You are creating a FormData object but you are not sending it within your Axios request.
In order to send the file and form data, you have to append everything to FormData object.
<script>
export default {
data() {
return {
fileSelected: null,
article: {},
};
},
methods: {
addArticle() {
var formData =new FormData();
formData.append('image', this.fileSelected);
formData.append('title', this.article.title);
axios
.post("http://localhost:8000/api/articles", formData)
.then((response) => this.$router.push({ name: "ArticlesList" }))
.catch((err) => console.log(err))
.finally(() => (this.loading = false));
}
,
selectedFile(event) {
this.fileSelected = event.target.files[0];
},
},
};
</script>
this worked in sending image files as string on database, hopefully it helps other people that are having similar problems
setup() {
const base64 = ref()
const changeFile= async(event) => {
const file = event.target.files[0];
base64.value = await convertBase64(file);
}
const convertBase64 = (file) => {
return new Promise ((resolve, reject) => {
const fileReader = new FileReader();
fileReader.readAsDataURL(file);
fileReader.onload = () => {
resolve(fileReader.result)
}
fileReader.onerror = (error) => {
reject(error)
}
})
}
const form = reactive({
title: " ",
body: " ",
image: base64,
})
const submitForm = () => {
axios.post("http://localhost:8000/api/articles", form)
}
return { changeFile, submitForm, form}
},
I have react-native 0.44.0 and react-native-fbsdk 0.5.0. ShareDialog component work fine, but due to lack of docs explanation had been totally stuck. I have app with own API. I make API call fetch sharing template with photos array.
.then((responseData) => {
console.log("Facebook Share Api Test")
console.log(responseData)
// After receiving result checking Platform
// If this is iOS we should let our result image links be fetched to encode it in Base64.
if(Platform.OS !== 'android'){
console.log("Not Andro!d!")
let imgUrl
let sharePhotoContent
let iteratePhotos = function (data) {
var photoInfo = [];
var ready = Promise.resolve(null)
data.forEach(function (value, i) {
let iconURL = API.SERVER_URL + API.SERVICE_PORT + API.HEAD_ICON_RES_URL + value.photo_id + 'S'
ready = ready.then(function () {
return RNFetchBlob
.fetch('GET', iconURL)
.then(res => res.data)
.then(resData => {
imgUrl = 'data:image/jpeg;base64,' + resData
console.log(imgUrl)
return imgUrl
})
.then(img => {
console.log(img)
let res = {
imageUrl: img,
userGenerated: true,
caption: value.comment
}
return res
})
.catch(err => {
console.log(err)
})
}).then(function (resData) {
photoInfo[i] = resData;
});
});
return ready.then(function () { return photoInfo; });
}
iteratePhotos(responseData.photos).then((res) => {
console.log('res', res)
if(res.length > 0){
sharePhotoContent = {
contentType: 'photo',
contentDescription: 'Wow, check out this great site!',
photos: res
}
} else {
sharePhotoContent = {
contentType: 'link',
contentUrl: 'some url',
message: responseData.message
}
}
ShareDialog.canShow(sharePhotoContent)
.then((canShow) => {
if (canShow) {
return ShareDialog.show(sharePhotoContent);
}
})
.then((result) => {
this.setState({isshowIndicator: false})
if(!result.isCancelled){
this.setState({isFacebookShared: true})
setTimeout(() => alert("Success!"), 100)
}
})
.catch(error => {
this.setState({isshowIndicator: false})
console.log(error)
setTimeout(() => alert('Share fail with error: ' + error), 100)
}
)
})
} else {
let photoInfo = responseData.photos.map(value => {
return {
imageUrl: API.SERVER_URL + API.SERVICE_PORT + API.HEAD_ICON_RES_URL + value.photo_id + 'S',
...value
}
})
console.log(photoInfo, "It IS ANDROID")
if(responseData.photos.length > 0){
var sharePhotoContent = {
contentType: 'photo',
photos: photoInfo
}
} else {
var sharePhotoContent = {
contentType: 'link',
contentUrl: 'some url',
message: responseData.message
}
}
ShareDialog.canShow(sharePhotoContent)
.then((canShow) => {
if (canShow) {
return ShareDialog.show(sharePhotoContent);
}
})
.then((result) => {
this.setState({isshowIndicator: false})
if(!result.isCancelled){
this.setState({isFacebookShared: true})
setTimeout(() => alert("Success!"), 100)
}
})
.catch(error => {
this.setState({isshowIndicator: false})
setTimeout(() => alert('Share fail with error: ' + error), 100)
})
}
})
When I tap share, sharedialog opens and photos that I want are pasted but message line waits to be filled
But I need into ShareDialog opened:
Photos needed to be attached;
Message to be prefilled according that one I received from my API.
Is this possible? Please help this is prerelease feature needed to be implemented very fast and I havent any idea how((
Attaching screenshots that describes 1. what is going now here? 2. What i want to do.
some social network like facebook does not support pre-filling the message for users as seen in their Policy: https://developers.facebook.com/policy/#socialplugins
I have a complete mess of a component. Right now I pass a function I have been trying a million things I can not make it work.
export default class DatafileUpload extends Component {
initialState = {
fileUploading: false,
fileList: [],
status: 'empty', // 'empty' | 'active' | 'success' | 'exception'
file: {}
}
state = this.initialState
static propTypes = {
userId: PropTypes.string.isRequired,
datasetId: PropTypes.string.isRequired
}
scrubFilename = (filename) => filename.replace(/[^\w\d_\-.]+/ig, '')
requestSignedS3Url = (file) => {
const filename = this.scrubFilename(file.name)
const params = {
userId: this.props.userId,
contentType: file.type,
Key: `${filename}`
};
return api.get('/s3/signUpload', { params })
.then(response => {
return response.data;
})
.catch(error => {
console.error(error);
});
}
uploadFile = (file) => {
this.requestSignedS3Url(file)
.then(signResult => this.uploadToS3(file, signResult))
.catch(error => console.log(error))
}
createCORSRequest = (method, url, opts) => {
opts = opts || {};
let xhr = new XMLHttpRequest();
if (xhr.withCredentials != null) {
xhr.open(method, url, true);
if (opts.withCredentials != null) {
xhr.withCredentials = opts.withCredentials;
}
} else if (typeof XDomainRequest !== "undefined") {
xhr = new XDomainRequest();
xhr.open(method, url);
} else {
xhr = null;
}
return xhr;
};
stepFunctions = () => {
return {
preprocess: (file) => {
console.log('Pre-process: ' + file.name);
},
onProgress: (percent, message, file) => {
this.setState({ fileUploading: true })
console.log('Upload progress: ' + percent + '% ' + message);
},
onFinish: (signResult) => {
this.setState({ fileUploading: false })
console.log("Upload finished: " + signResult.publicUrl)
},
onError: (message) => {
this.setState({ fileUploading: false })
console.log("Upload error: " + message);
},
scrubFilename: (filename) => {
return filename.replace(/[^\w\d_\-\.]+/ig, '');
},
onFinishS3Put: (signResult, file) => {
console.log(signResult)
return console.log('base.onFinishS3Put()', signResult.publicUrl);
}
}
}
uploadToS3 = async (file, signResult) => {
const xhr = await this.createCORSRequest('PUT', signResult.signedUrl);
const functions = this.stepFunctions()
functions.preprocess(file)
if (!xhr) {
functions.onError('CORS not supported', file);
} else {
xhr.onload = () => {
if (xhr.status === 200) {
functions.onProgress(100, 'Upload completed', file);
return functions.onFinishS3Put('potatopotato', file);
} else {
return functions.onError('Upload error: ' + xhr.status, file);
}
};
xhr.onerror = () => {
return functions.onError('XHR error', file);
};
xhr.upload.onprogress = (e) => {
let percentLoaded;
if (e.lengthComputable) {
percentLoaded = Math.round((e.loaded / e.total) * 100);
return functions.onProgress(percentLoaded, percentLoaded === 100 ? 'Finalizing' : 'Uploading', file);
}
};
}
xhr.setRequestHeader('Content-Type', file.type);
if (signResult.headers) {
const signResultHeaders = signResult.headers
Object.keys(signResultHeaders).forEach(key => {
const val = signResultHeaders[key];
xhr.setRequestHeader(key, val);
})
}
xhr.setRequestHeader('x-amz-acl', 'public-read');
this.httprequest = xhr;
return xhr.send(file);
};
handleChange = ({ file, fileList }) => {
const functions = this.stepFunctions()
functions.preprocess(file)
if (!file) {
functions.onError('CORS not supported', file);
} else {
file.onload = () => {
if (file.status === 200) {
functions.onProgress(100, 'Upload completed', file);
return functions.onFinishS3Put('potatopotato', file);
} else {
return functions.onError('Upload error: ' + file.status, file);
}
};
file.onerror = () => {
return functions.onError('XHR error', file);
};
file.upload.onprogress = (e) => {
let percentLoaded;
if (e.lengthComputable) {
percentLoaded = Math.round((e.loaded / e.total) * 100);
return functions.onProgress(percentLoaded, percentLoaded === 100 ? 'Finalizing' : 'Uploading', file);
}
};
}
console.log('File: ', file)
// always setState
this.setState({ fileList });
}
render() {
const props = {
onChange: this.handleChange,
multiple: true,
name: "uploadFile",
defaultFileList: this.initialState.fileList,
data: this.uploadFile,
listType: "text",
customRequest: ????,
showUploadList: {
showPreviewIcon: true,
showRemoveIcon: true
},
onProgress: ( {percent} ) => {
this.setState({ fileUploading: true })
console.log('Upload progress: ' + percent + '% ' );
},
onError: (error, body) => {
this.setState({ fileUploading: false })
console.log("Upload error: " + error);
},
onSuccess: (body)=> {
console.log(body)
return console.log('base.onFinishS3Put()');
}
};
return (
<Upload {...props} fileList={this.state.fileList}>
<Button>
<Icon type="upload" /> Upload
</Button>
</Upload>
)
}
}
I know this code is a mess that doesn't make sense and have duplicated data all around. I want it to make it work and then clean up/optimse. Basically I am not able to make the component progress bar update nor with the onChange nor when I am trying to use the customRequest. When is customRequest called? This is not very abundant in explanations... I don't understand how does it do the replacement of Ajax upload.
I was struggling with that as well and then I found your question.
So the way I found to use customRequest and onChange is:
<Upload name="file" customRequest={this.customRequest} onChange={this.onChange}>
<Button>
<Icon type="upload" /> Click to Upload
</Button>
</Upload>
...
onChange = (info) => {
const reader = new FileReader();
reader.onloadend = (obj) => {
this.imageDataAsURL = obj.srcElement.result;
};
reader.readAsDataURL(info.file.originFileObj);
...
};
...
customRequest = ({ onSuccess, onError, file }) => {
const checkInfo = () => {
setTimeout(() => {
if (!this.imageDataAsURL) {
checkInfo();
} else {
this.uploadFile(file)
.then(() => {
onSuccess(null, file);
})
.catch(() => {
// call onError();
});
}
}, 100);
};
checkInfo();
};
There are probably better ways to do it, but I hope that helps you.
I struggled it a lot and find an efficient way to handle this case.
first- you should mess with the customRequest only when you need to change to body and the request type (like using post instead of 'put' or using xml or add another extra header).
for the signing Url you can send in the action prop callback which return a promise with the right Url to upload like:
handleUplaod = (file: any) => {
return new Promise(async (resolve, reject) => {
const fileName = `nameThatIwant.type`;
const url = await S3Fetcher.getPresignedUrl(fileName);
resolve(url);
});
and render like:
render(){
return(
....
<Upload
action={this.handleUplaod}
....
Upload>
the uploader take the url from the action prop.
the onChange method which is provided also will be called any time the status of upload is changed-
onChange# The function will be called when uploading is in progress,
completed or failed.
When uploading state change, it returns:
{ file: { /* ... / }, fileList: [ / ... / ], event: { / ...
*/ }, }
when upload started
you will need to activate the file reader from that.
like:
....
fileReader = new FileReader();
.....
onChange = (info) => {
if (!this.fileReader.onloadend) {
this.fileReader.onloadend = (obj) => {
this.setState({
image: obj.srcElement.result, //will be used for knowing load is finished.
});
};
// can be any other read function ( any reading function from
// previously created instance can be used )
this.fileReader.readAsArrayBuffer(info.file.originFileObj);
}
};
notice when completed stage that event=undefind
To update the UI from the upload events you should use the options variables from customRequest and call them whenever you need.
onSuccess- should be called when you finish uploading and it will change the loading icon to the file name.
onError- will paint the file name filed to red.
onProgress- will update the progress bar and should be called with {percent: [NUMBER]} for updating.
for example in my code-
customRequest = async option => {
const { onSuccess, onError, file, action, onProgress } = option;
const url = action;
await new Promise(resolve => this.waitUntilImageLoaded(resolve)); //in the next section
const { image } = this.state; // from onChange function above
const type = 'image/png';
axios
.put(url, Image, {
onUploadProgress: e => {
onProgress({ percent: (e.loaded / e.total) * 100 });
},
headers: {
'Content-Type': type,
},
})
.then(respones => {
/*......*/
onSuccess(respones.body);
})
.catch(err => {
/*......*/
onError(err);
});
};
waitUntilImageLoaded = resolve => {
setTimeout(() => {
this.state.image
? resolve() // from onChange method
: this.waitUntilImageLoaded(resolve);
}, 10);
};
I used axios but you can use other libraries as well
and the most important part-
render(){
return(
....
<Upload
onChange={this.onChange}
customRequest={this.customRequest}
...>
I am trying to upload a file to web api which takes the file as byte array using angular 2 application.
I am not able to pass the byte array from angular 2 page to web api. It looks like the File Reader read method is asynchronous. How do I make this as synchronous call or wait for the file content to be loaded before executing the next line of code?
Below is my code
//attachment on browse - when the browse button is clicked
//It only assign the file to a local variable (attachment)
fileChange = (event) => {
var files = event.target.files;
if (files.length > 0) {
this.attachment = files[0];
}
}
//when the submit button is clicked
onSubmit = () => {
//Read the content of the file and store it in local variable (fileData)
let fr = new FileReader();
let data = new Blob([this.attachment]);
fr.readAsArrayBuffer(data);
fr.onloadend = () => {
this.fileData = fr.result; //Note : This always "undefined"
};
//build the attachment object which will be sent to Web API
let attachment: Attachment = {
AttachmentId: '0',
FileName: this.form.controls["attachmentName"].value,
FileData: this.fileData
}
//build the purchase order object
let order: UpdatePurchaseOrder = {
SendEmail: true,
PurchaseOrderNumber: this.form.controls["purchaseOrderNumber"].value,
Attachment: attachment
}
//call the web api and pass the purchaseorder object
this.updatePoService
.updatePurchaseOrder(this.form.controls["purchaseOrderRequestId"].value, order)
.subscribe(data => {
if (data) {
this.saveSuccess = true;
}
else {
this.saveSuccess = false;
}
},
error => this.errors = error,
() => this.res = 'Completed'
);
}
Any hint would be useful.
regards,
-Alan-
You cannot make this async call synchronous. But you can take advantage of the observables to wait for the files to be read:
//when the submit button is clicked
onSubmit = () => {
let file = Observable.create((observer) => {
let fr = new FileReader();
let data = new Blob([this.attachment]);
fr.readAsArrayBuffer(data);
fr.onloadend = () => {
observer.next(fr.result);
observer.complete()
};
fr.onerror = (err) => {
observer.error(err)
}
fr.onabort = () => {
observer.error("aborted")
}
});
file.map((fileData) => {
//build the attachment object which will be sent to Web API
let attachment: Attachment = {
AttachmentId: '0',
FileName: this.form.controls["attachmentName"].value,
FileData: fileData
}
//build the purchase order object
let order: UpdatePurchaseOrder = {
SendEmail: true,
PurchaseOrderNumber: this.form.controls["purchaseOrderNumber"].value,
Attachment: attachment
}
return order;
})
.switchMap(order => this.updatePoService.updatePurchaseOrder(this.form.controls["purchaseOrderRequestId"].value, order))
.subscribe(data => {
if (data) {
this.saveSuccess = true;
} else {
this.saveSuccess = false;
}
},
error => this.errors = error,
() => this.res = 'Completed'
);
}
I arrived here looking for a solution for a similar issue. I'm performing requests to an endpoint which can response a binary blob if anything goes well or a JSON file in event of error.
this.httpClient.post(urlService, bodyRequest,
{responseType: 'blob', headers: headers})
.pipe(map((response: Response) => response),
catchError((err: Error | HttpErrorResponse) => {
if (err instanceof HttpErrorResponse) {
// here, err.error is a BLOB containing a JSON String with the error message
} else {
return throwError(ErrorDataService.overLoadError(err, message));
}
}));
As FileReaderSync apparently doesn't work in Angular6 I took n00dl3's solution (above) to throw the error after parsing the Blob content:
return this.httpClient.post(urlService, bodyRequest,
{responseType: 'blob', headers: headers})
.pipe(map((response: Response) => response),
catchError((err: Error | HttpErrorResponse) => {
const message = `In TtsService.getTts(${locale},${outputFormat}). ${err.message}`;
if (err instanceof HttpErrorResponse) {
const $errBlobReader: Observable<HttpErrorResponse> = Observable.create((observer) => {
const fr = new FileReader();
const errorBlob = err.error;
fr.readAsText(errorBlob, 'utf8');
fr.onloadend = () => {
const errMsg = JSON.parse(fr.result).message;
const msg = `In TtsService.getTts(${locale},${outputFormat}). ${errMsg}`;
observer.error(ErrorDataService.overLoadError(err, msg));
};
fr.onerror = (blobReadError) => {
observer.error(blobReadError);
};
fr.onabort = () => {
observer.error('aborted');
};
});
return $errBlobReader;
} else {
return throwError(ErrorDataService.overLoadError(err, message));
}
}));
Thanks! You really saved my day!