Uploading files using js fileReader results in corrupted files - javascript

I am trying to upload a file using js file reader and AJAX to my server.
I used FileAPI and fileReader to read the file and convert it to string and then send it to the server via an AJAX request.
Here is my client side js code :
function upload() {
var file = document.getElementById('periodExcel').files[0];
if (file) {
console.log(file);
var reader = new FileReader();
reader.readAsText(file, file.type);
console.log(reader);
reader.onload = uploadAndRun;
}
}
function uploadAndRun(event) {
console.log(event);
var result = event.target.result;
$('#js').html(result);
var fileName = document.getElementById('periodExcel').files[0].name; //Should be 'picture.jpg'
$.post('./upload.php', {data: result, name: fileName}, function(result2){
$('#php').html(result2);
});
}
Here is the upload php script:
file_put_contents('upload/' . $_POST['name'], $_POST['data']);
it just write the file using php file_put_contents function.
My problem is that the uploaded file is corrupted and has a different size than the original file (it is larger).
I tried to use php file_get_contents function to read the same file and write it again using file_put_contents and the result file was fine and same as the original one.
I then tried to compare the two strings (the one that comes from the file reader and the one that comes from file_get_contents ) and compares the two strings using strcmp, that gives me that the string that come from the fileReader is larger than the one comes from file_get_contents.
So, what is the problem with my code and how to use the FileReader to upload file in this way while using readAsText function.

You are using the wrong collection in PHP. To access uploaded file stream use $_FILES.
See here:
http://php.net/manual/en/reserved.variables.files.php
and here: http://php.net/manual/en/features.file-upload.post-method.php
In short, PHP runtime takes care of reading the upload stream from the HTTP request, stores it locally in a temp folder and exposes the above map for you to access the temp file and possibly move it to another location (or do whatever else you need to do with it).

Related

Issue in Reading Excel (xlsx) File Data in JavaScript/SAPUI5

I am trying to read a .xlsx file in my SAPUI5 application using FileReader api. The UI5 control that I am using to upload the file is sap.ui.unified.FileUploader
My requirement is to read the content of the Excel sheet and display its data in a table in my UI5 application.
For this, I create a FileReader instance and call its .readAsBinaryString or .readAsText method and pass my file to it.
When the content is read, inside the onload event, the content of the file is displayed in an unreadable format (refer below)
Am I missing something? Or is there a different way of reading Excel data?
Thanks to mkysoft's and this answer. I was able to read the .xlsx file content.
For this, I had to create two files jszip.js and xlsx.js in my project. I created a separate folder for them and mentioned the two files' locations in sap.ui.define section in my controller.
The two libraries are available at
CDNJS - JSZip3 and
CDNJS - XLSX4 locations.
sap.ui.define([
"sap/ui/core/mvc/Controller",
.... //other libraries
"projectnamespace/foldername/jszip",
"projectnamespace/foldername/xlsx"
], function (Controller,...., jszip, xlsx)
And then, to read the .xlsx file I added the following code:
var oFileUploader = sap.ui.getCore().byId("fileUploader"); // get the sap.ui.unified.FileUploader
var file = oFileUploader.getFocusDomRef().files[0]; // get the file from the FileUploader control
if (file && window.FileReader) {
var reader = new FileReader();
reader.onload = function (e) {
var data = e.target.result;
var excelsheet = XLSX.read(data, {
type: "binary"
});
excelsheet.SheetNames.forEach(function (sheetName) {
var oExcelRow = XLSX.utils.sheet_to_row_object_array(excelsheet.Sheets[sheetName]); // this is the required data in Object format
var sJSONData = JSON.stringify(oExcelRow); // this is the required data in String format
});
};
reader.readAsBinaryString(file);
}
You need to parse Excel file in client side or server side. Excel files are not plain text. Old excel files (xls) can not parse without Excel, it has own binary format. But you can parse xlsx files, these are zipped multiple file which contains raw data, image and style files.
You can use exsting js library for client parse, example post How to parse Excel file in Javascript/HTML5.
You can use abap2xlsx library for ABAP backend.

Using btoa to parse xls from javascript to C#

I have written a program that sends an excel file uploaded by the user from javascript to C# where it is then formatted. The code works well for .xlsx files; however, when I try to parse .xls files, I receive the following error:
System.ArgumentNullException: 'Value cannot be null. Parameter name:
s'.
After some further testing with breakpoints, I believe I have found the problem, but I cannot find a solution. The following code is the javascipt used to encode the file:
document.getElementById('selectedFile').addEventListener('change', function (event) {
var reader = new FileReader();
reader.onload = function () {
filename = event.target.files[0].name;
var uint8Array = new Uint8Array(this.result);
fileContent = btoa(String.fromCharCode.apply(null, uint8Array));
}
reader.readAsArrayBuffer(this.files[0]);
}, false);
When the file uploaded is of type .xlsx, fileContent returns the correct value; however, if an .xls file is uploaded, it returns null, therefore breaking the code further on.
Is there a way to make this code work for .xls files? Alternatively, is there a way I can convert it to .xlsx before I parse it to the backend?
.xlsx is a text format (XML) so String.fromCharCode should work appropriately.
.xls is a binary file format. I would not expect String functions to work properly with it.
You can use Excel to convert between formats. The conversion is non-trivial and, while not impossible to do from javascript, probably not worth spending a couple months on.

Edit a file to be uploaded

I would like to be able to edit a file that has been selected for upload. I want to search and replace text in case absolute files should be made relative...
I notice in the File API I can do some of it, but I get a little stuck:
document.getElementById('exampleInputFile').onchange = function(event) {
var fileToLoad = event.target.files[0];
if (fileToLoad) {
var reader = new FileReader();
reader.onload = function(fileLoadedEvent) {
var textFromFileLoaded = fileLoadedEvent.target.result;
//Use logic to remove absolute files
//Upload S3
};
reader.readAsText(fileToLoad, 'UTF-8');
}
};
I am trying to figure out how now to convert that text to a proper File so that I can upload it to S3 using an existing api that expects something returned by: event.target.files[0] code above.
I do not want the server to handle any heavy lifting here if I can avoid it (files can easily be a few megabytes since they can be 3D models).
Assuming you know the url of the file when it lands in the S3 bucket, you can retrieve the file using a http.get, which will give you the contents of the (I assume plain text file). You can then parse that file and do whatever modification you need to do on the contents. If the file has changed, you can then write it back to the S3 bucket to replace the original file.
On AWS you can use Lambda to execute NodeJS code when an event is triggered (for example an upload to a specified bucket).

Compress dcm files with JSZip

im currently working on a dicom file upload system that uploads .dcm files with jquery file uploader. It is working fine but as DICOM data-sets can get very large i want to compress the files with JSZip before the upload.
Simply i am passing the file object to a zip function that returns the zipped file object. This is working fine with commonly known files but not with DICOM files. I've already tried to encode the files to base64 string before zipping but that doesn't work either.
JSZip always throws me the following error:
Uncaught Error: The data of 'IM-0001-0001.dcm' is in an unsupported format !
I am using the following file compress function:
compressFile: function(data) {
var zip = new JSZip();
var file = data.files[0];
zip.file(file.name, file, {binary:false});
content = zip.generate({
compression: 'DEFLATE'
});
return content;
}
I have also tried with base64 and binary in the .file options but that didn't made the trick.
Has anyone a clue on how to get that working? Im a beginner to JS so im sorry for noobish questions ^^
Kind Regards
You need to use a FileReader to read the content of data.files[0] first:
var reader = new FileReader();
reader.onload = (function(e) {
var zip = new JSZip(e.target.result);
var result = zip.generate({
compression: 'DEFLATE'
});
// do something with result
}
reader.readAsArrayBuffer(data.files[0]);
See also this example.
Warning, FileReader is asynchronous: you can't make your function returns the result.

JPEG data obtained from FileReader doesn't match data in file

I'm trying to select a local JPEG file in the web browser via the HTML5 FileReader so I can submit it to a server without reloading the page. All the mechanics are working and I think I'm transferring and saving the exact data that JavaScript gave me, but the result is an invalid JPEG file on the server. Here's the basic code that demonstrates the problem:
<form name="add_photos">
​<input type=​"file" name=​"photo" id=​"photo" /><br />
​<input type=​"button" value=​"Upload" onclick=​"upload_photo()​;​" />​
</form>
<script type="text/javascript">
function upload_photo() {
file = document.add_photos.photo.files[0];
if (file) {
fileReader = new FileReader();
fileReader.onload = upload_photo_ready;
fileReader.readAsBinaryString(file);
}
}
function upload_photo_ready(event) {
data = event.target.result;
// alert(data);
URL = "submit.php";
ajax = new XMLHttpRequest();
ajax.open("POST", URL, 1);
ajax.setRequestHeader("Ajax-Request", "1");
ajax.send(data);
}
</script>
Then my PHP script does this:
$data = file_get_contents("php://input");
$filename = "test.jpg";
file_put_contents($filename, $data);
$result = imagecreatefromjpeg($filename);
That last line throws a PHP error "test.jpg is not a valid JPEG file." If I download the data back to my Mac and try to open it in Preview, Preview says the file "may be damaged or use a file format that Preview doesn’t recognize."
If I open both the original file on my desktop and the uploaded file on the server in text editors to inspect their contents, they are almost but not quite the same. The original file starts like this:
ˇÿˇ‡JFIFˇ˛;CREATOR: gd-jpeg v1.0 (using IJG JPEG v62), quality = 90
But the uploaded file starts like this:
ÿØÿàJFIFÿþ;CREATOR: gd-jpeg v1.0 (using IJG JPEG v62), quality = 90
Interestingly, if I view the data in a JavaScript alert with the commented-out line above, it looks just like the uploaded file's data, so it seems as if the FileReader isn't giving the correct data at the very beginning, as opposed to a problem that is introduced while transferring or saving the data on the server. Can anyone explain this?
I'm using Safari 6 and I also tried Firefox 14.
UPDATE: I just figured out that if I skip the FileReader code and change ajax.send(data) to ajax.send(file), the image is transferred and saved correctly on the server. So my problem is basically solved, but I'll award the answer points to anyone who can explain why my original approach with readAsBinaryString didn't work.
Your problem lies with readAsBinaryString. This will transfer the binary data byte-for-byte into a string, so that you will send a text string to your PHP file. Now a text string always has an encoding; and when you use XmlHttpRequest to upload a string, by default it will use UTF-8.
So each character, which was originally supposed to represent one byte, will be encoded as UTF-8... which uses multiple bytes for each character with a code point above 127!
Your best best is to use readAsArrayBuffer instead of readAsBinaryString. This will avoid all the character set conversions (that are necessary when dealing with strings).

Categories

Resources