First time with React-Admin. I am using it to create a panel that basically monitors some parameters that I get from an API. However, one section requires a .csv file to be uploaded. I am trying to implement it with FileInput but I am unable to catch the file. I don't understand how to do it.
File selection step (from pc to browser) is working properly, but my problem is that I cannot handle the file after that step. I read the docs, but I don't know how to do it. I tried many different ways but I am getting crazy.
Below is the basic code. I guess I have to add a handler or something similar but, how? I have little experience with React too. I know the basics, but I just built a couple of (super) simple apps. Just for learn.
// UploadFile.js
...
export const UploadSection = props => (
<SimpleForm>
<FileInput source="csvFile" label="Upload file (.csv)" accept="text/csv" >
<FileField source="src" title="title" />
</FileInput>
</SimpleForm>
);
// App.js
...
const App = () => (
<Admin dataProvider={dataProvider} authProvider={authProvider} >
...
<Resource name="uploadSection" list={UploadSection} />
...
</Admin>
);
The question:
How can I catch the .csv file?
Thanks in advance!
After working on it during hours I got it working.
First problem (the one I commented #selens answer): I got Uncaught TypeError: _this.props.save is not a function because I wasn't working in Create or Edit View. It seems you need to use FileInput in Create or Edit Views. If not, Save button won't work.
From docs:
save: The function invoked when the form is submitted. This is passed automatically by react-admin when the form component is used inside Create and Edit components.
Second problem: In my case I upload one file at a time (multiple={false} in FileInput). However, the code addUploadFeature.js is ready to use with multiple files. So, I edited part of addUploadFeature.js to upload just one file. See the complete file below.
// addUploadFeature.js
const convertFileToBase64 = file => new Promise((resolve, reject) => {
const reader = new FileReader();
reader.readAsDataURL(file.rawFile);
reader.onload = () => resolve(reader.result);
reader.onerror = reject;
});
const addUploadFeature = requestHandler => (type, resource, params) => {
if (type === 'UPDATE' && resource === 'myResource') {
if (params.data.myFile) {
// NEW CODE HERE (to upload just one file):
const myFile = params.data.myFile;
if ( !myFile.rawFile instanceof File ){
return Promise.reject('Error: Not a file...'); // Didn't test this...
}
return Promise.resolve( convertFileToBase64(myFile) )
.then( (picture64) => ({
src: picture64,
title: `${myFile.title}`
}))
.then( transformedMyFile => requestHandler(type, resource, {
...params,
data: {
...params.data,
myFile: transformedMyFile
}
}));
}
}
return requestHandler(type, resource, params);
};
export default addUploadFeature;
After all, I was able to send the file to the server in Base64 format.
Hope this to be useful for somebody in the future ;)
I have a similar problem, I cannot upload a file and issue will be in accept prop.
You can use the following code:
<FileInput source="csvFile" label="Upload file (.csv)" accept=".csv" >
Instead of:
<FileInput source="csvFile" label="Upload file (.csv)" accept="text/csv" >
Have you checked the DataProvider and UploadFeature sections in the documentation?
If you have your own DataProvider, you can create a new file addUploadFeature.js and customize it as in the sample under this link:
https://marmelab.com/react-admin/DataProviders.html#extending-a-data-provider-example-of-file-upload
Related
I have set Dropzone to accept only this files:
acceptedFiles:
"application/vnd.openxmlformats-officedocument.wordprocessingml.document, application/msword",
also tried in this other two ways:
acceptedFiles: ".docx, .doc",
acceptedFiles:
"application/vnd.openxmlformats-officedocument.wordprocessingml.document, application/msword, .docx, .doc",
but isn't working with drag&drop. It's working only with the "clickable" option, so the fileInput is doing his job.
The function getAcceptedFiles() returns an empty array []. It is only the visual impact that shows the dropped files. Am I missing something? I have to manually exclude them from the preview?
Thanks for you help.
Seems that there isn't a default implementation for this. You must implement the logic for the drag&drop.
const maxFilesNumber = 1;
//after complete adding files, remove if not accepted or maxFilesNumber reached
dropzone.on("complete", (file) => {
if (!file.accepted || dropzone.files.length > maxFilesNumber)
dropzone.removeFile(file);
});
dropzone.on("maxfilesreached", () => dropzone.disable() );
//reset event is fired when files in dropzone are 0
dropzone.on("reset", () => dropzone.enable() );
I know that for CKEditor 4, you can get the textarea data like this:
var content = CKEDITOR.instances['comment'].getData();
How is this done for CKEditor 5?
You can find the answer in the Basic API guide.
Basically, in CKEditor 5 there's no single global editors repository (like the old CKEDITOR.instances global variable). This means that you need to keep the reference to the editor that you created and use that reference once you'll want to retrieve the data:
ClassicEditor
.create( document.querySelector( '#editor' ) )
.then( editor => {
editor.getData(); // -> '<p>Foo!</p>'
} )
.catch( error => {
console.error( error );
} );
If you need to retrieve the data on some other occasions (who would read it just after initializing the editor, right? ;)), then save the reference to the editor in some shared object of your application's state or some variable in the scope:
let theEditor;
ClassicEditor
.create( document.querySelector( '#editor' ) )
.then( editor => {
theEditor = editor; // Save for later use.
} )
.catch( error => {
console.error( error );
} );
function getDataFromTheEditor() {
return theEditor.getData();
}
See this JSFiddle: https://jsfiddle.net/2h2rq5u2/
EDIT: If you need to manage more than one editor instance, see CKEDITOR 5 get editor instances.
Declare a global variable and then use editor.getData(). Something like this:
var editor;
ClassicEditor
.create(document.querySelector('#editor'))
.then(editor => {
editor=editor;
})
.catch(error => {
console.error(error);
});
Then, in your event handler, this should work:
editor.getData();
I ran into what felt like a unique situation where I was loading multiple instances of CKeditor5 on a page but I also had features to dynamically add/remove/rearrange text areas that had editors applied to them.
Creating an array that contained all the instances was manageable but also a bit more complicated to keep track of when adding/removing/rearrange editor instances.
I instead decided to obtain the innerHTML of the editor instances by selecting all .ck-editor__editable classes and iterating over them to check the content.
document.querySelector("button[type='submit']").addEventListener('click', (event) => {
event.preventDefault();
const domEditableElements = document.querySelectorAll('.ck-editor__editable');
for (let i = 0; i < domEditableElements.length; ++i) {
let elementData = domEditableElements[i].innerHTML
if (elementData === '<p><br data-cke-filler="true"></p>') {
console.log(`Field ${i} is empty`);
}
}
});
Console logging indicated that ck-editor stored <p><br data-cke-filler="true"></p> in blank fields.
I have an app where having the DateTimeOriginal time stamp on photos are absolutely necessary. Is there a way for me to stop uploading and display a message using Fine Uploader?
I've never heard of the "taken-at" tag, and I don't believe this is a standard field. The rest of this answer assumes you really do want to focus on this tag, but even if you don't you can make a simple change in the source code below to focus on another EXIF tag instead.
One approach is to check each file in an onSubmit callback handler and simply reject the file is it does not contain a "taken-at" field. The following example utilizes the exif-js library to parse an image file's EXIF data:
var uploader = new qq.FineUploader({
callbacks: {
onSubmit: function(id) {
var blob = this.getFile(id)
return new Promise(function(resolve, reject) {
EXIF.getData(blob, function() {
var takenAt = EXIF.getTag(this, 'taken-at')
if (takenAt) {
resolve()
}
else {
reject()
}
})
})
}
}
})
I'm trying to upload data captured in a local html file on an iPad and save it to server.
I found this: Sending data to an external file via Ajax
So as far as I can understand, there is no way to send the info doing something like this:
ajax.open("POST",'http://www.misite.com/canvas/testSave.php',true);
from a html on the iPad, I'm right?
So I just want to know if anyone knows a trick to do this. Thanks!
After a couple of weeks this is what I could achieved.
1.- The function that sends the data (an image generated from a canvas):
function sendImageData()
{
var filename = $("#filename").val().trim();
if(filename == ''){
alert("File name is needed");
return;
}
var uploadCanvas = $("#uploadCanvas");
var canvasData = uploadCanvas[0].toDataURL("image/png");
var debugConsole= $("#debugConsole");
debugConsole.val(canvasData);
$.ajax({
type: 'POST',
url: "http://yourremoteserver.com/canvas/save.php",
data: {
canvasData:canvasData,
filename:filename
}
}).done(function() {
alert("saved: " + filename + ".png");// THIS IS NOT WORKING YET.
}
);
}
2.- The PHP that receives and saves the data:
<?php
$imagen = $_POST['canvasData'];
$filename = $_POST['filename'];
if (isset($imagen)){
$imageData=$imagen;
$filteredData=substr($imageData, strpos($imageData, ",")+1);
$unencodedData=base64_decode($filteredData);
$fp = fopen( $filename.'.png', 'wb' );
fwrite( $fp, $unencodedData);
fclose( $fp );
}
?>
If anyone can help me with the .done function to work (remember, the html file is in an ipad and the php on a server) let me know. Cheers.
I have used Plupload for this purpose. It automagically switches between flash, silverlight, and html5 so it should work on just about any browser (including the safari on the iPad). Basically, it uploads a file to a processing script with some generated id (it generates it for you). Then, you can poll another page to get the uploaded data once it is finished uploading.
EDIT: Re-reading your post I am not sure how pertinent this is since it requires the user to select a file and I'm not sure that's what you are getting at exactly.
It seems like I have not clearly communicated my problem. I need to send a file (using AJAX) and I need to get the upload progress of the file using the Nginx HttpUploadProgressModule. I need a good solution to this problem. I have tried with the jquery.uploadprogress plugin, but I am finding myself having to rewrite much of it to get it to work in all browsers and to send the file using AJAX.
All I need is the code to do this and it needs to work in all major browsers (Chrome, Safari, FireFox, and IE). It would be even better If I could get a solution that will handle multiple file uploads.
I am using the jquery.uploadprogress plugin to get the upload progress of a file from the NginxHttpUploadProgressModule. This is inside an iframe for a facebook application. It works in firefox, but it fails in chrome/safari.
When I open the console I get this.
Uncaught ReferenceError: progressFrame is not defined
jquery.uploadprogress.js:80
Any idea how I would fix that?
I would like to also send the file using AJAX when it is completed. How would I implement that?
EDIT:
I need this soon and it is important so I am going to put a 100 point bounty on this question. The first person to answer it will receive the 100 points.
EDIT 2:
Jake33 helped me solve the first problem. First person to leave a response with how to send the file with ajax too will receive the 100 points.
Uploading files is actually possible with AJAX these days. Yes, AJAX, not some crappy AJAX wannabes like swf or java.
This example might help you out: https://webblocks.nl/tests/ajax/file-drag-drop.html
(It also includes the drag/drop interface but that's easily ignored.)
Basically what it comes down to is this:
<input id="files" type="file" />
<script>
document.getElementById('files').addEventListener('change', function(e) {
var file = this.files[0];
var xhr = new XMLHttpRequest();
(xhr.upload || xhr).addEventListener('progress', function(e) {
var done = e.position || e.loaded
var total = e.totalSize || e.total;
console.log('xhr progress: ' + Math.round(done/total*100) + '%');
});
xhr.addEventListener('load', function(e) {
console.log('xhr upload complete', e, this.responseText);
});
xhr.open('post', '/URL-HERE', true);
xhr.send(file);
});
</script>
(demo: http://jsfiddle.net/rudiedirkx/jzxmro8r/)
So basically what it comes down to is this =)
xhr.send(file);
Where file is typeof Blob: http://www.w3.org/TR/FileAPI/
Another (better IMO) way is to use FormData. This allows you to 1) name a file, like in a form and 2) send other stuff (files too), like in a form.
var fd = new FormData;
fd.append('photo1', file);
fd.append('photo2', file2);
fd.append('other_data', 'foo bar');
xhr.send(fd);
FormData makes the server code cleaner and more backward compatible (since the request now has the exact same format as normal forms).
All of it is not experimental, but very modern. Chrome 8+ and Firefox 4+ know what to do, but I don't know about any others.
This is how I handled the request (1 image per request) in PHP:
if ( isset($_FILES['file']) ) {
$filename = basename($_FILES['file']['name']);
$error = true;
// Only upload if on my home win dev machine
if ( isset($_SERVER['WINDIR']) ) {
$path = 'uploads/'.$filename;
$error = !move_uploaded_file($_FILES['file']['tmp_name'], $path);
}
$rsp = array(
'error' => $error, // Used in JS
'filename' => $filename,
'filepath' => '/tests/uploads/' . $filename, // Web accessible
);
echo json_encode($rsp);
exit;
}
Here are some options for using AJAX to upload files:
AjaxFileUpload - Requires a form element on the page, but uploads the file without reloading the page. See the Demo.
List of JQuery Plug-ins Tagged With "File"
Uploadify - A Flash-based method of uploading files.
How can I upload files asynchronously?
Ten Examples of AJAX File Upload - This was posted this year.
UPDATE: Here is a JQuery plug-in for Multiple File Uploading.