0

I am using a JavaScript /AngularJS code excerpt from an article I found online. I made a couple of tweaks to it, because the original post would not work on IE 11, but other than that I am using it as I found it. This piece of code, allows you to upload and read an Excel file to a jQuery data table.

There is one requirement that I am missing and would like to ask for help, if possible. that requirement is to only allow Excel files to be uploaded, an no other type of file should be "visible" to the user.

Here is the code I am using:

AngularJS controller:

var app = angular.module('MyApp', []);
app.controller('MyController', ['$scope', '$http', function ($scope, $http) {

    $scope.SelectedFileForUpload = null;

    $scope.UploadFile = function (files) {
        $scope.$apply(function () {   
            $scope.Message = "";
            $scope.SelectedFileForUpload = files[0];
        })
    }

    //Parse Excel Data   
    $scope.ParseExcelDataAndSave = function () {

        var file = $scope.SelectedFileForUpload;

        if (file) {

            var reader = new FileReader();

            reader.onload = function (e) {

                var filename = file.name;
                // pre-process data
                var binary = "";
                var bytes = new Uint8Array(e.target.result);
                var length = bytes.byteLength;

                for (var i = 0; i < length; i++) {
                    binary += String.fromCharCode(bytes[i]);
                }

                // call 'xlsx' to read the file
                var data = e.target.result;
                var workbook = XLSX.read(binary, { type: 'binary', cellDates: true, cellStyles: true });
                var sheetName = workbook.SheetNames[0];
                var excelData = XLSX.utils.sheet_to_row_object_array(workbook.Sheets[sheetName]);

                if (excelData.length > 0) {
                    //Save data   
                    $scope.SaveData(excelData);
                }
                else {
                    $scope.Message = "No data found";
                }            };



            }
            reader.onerror = function (ex) {
                console.log(ex);
            }

            reader.readAsArrayBuffer(file);
        }

html view:

<body ng-app="MyApp">
    <div class="container py-4" ng-controller="MyController">
        <div class="card">
            <div class="card-header bg-primary text-white">
                <h5>Common Council List</h5>
            </div>
            <div class="card-body">

                @* Upload Button *@
                <button style="margin-bottom:10px;" type="button" class="btn btn-primary rounded-0" data-toggle="modal" data-target="#myModal">
                    <i class="fa fa-file-excel-o"></i> Upload Excel File
                </button>

                @* Modal Window *@
                <div class="modal" id="myModal">
                    <div class="modal-dialog">
                        <div class="modal-content">
                            <div class="modal-header">
                                <h4 class="modal-title">Upload Common Council Data</h4>
                                <button type="button" class="close" data-dismiss="modal">×</button>
                            </div>
                            <div class="modal-body">
                                <div class="col-md-12">
                                    <div class="input-group">
                                        <div class="custom-file">
                                            <input type="file" name="file" class="custom-file-input" onchange="angular.element(this).scope().UploadFile(this.files)" />
                                            <label class="custom-file-label" for="inputGroupFile04">Choose file</label>
                                        </div>
                                        <div class="input-group-append">
                                            <button class="btn btn-outline-secondary" type="button" ng-disabled="!SelectedFileForUpload" ng-click="ParseExcelDataAndSave()"><i class="fa fa-upload"></i> Upload</button>
                                        </div>
                                    </div>
                                    <span class="text-success">
                                        {{Message}}
                                    </span>
                                </div>
                            </div>
                            <div class="modal-footer">
                                <button type="button" class="btn btn-danger rounded-0" data-dismiss="modal" ng-click="clearText()">Close</button>
                            </div>
                        </div>
                    </div>
                </div>



                @* Main Table *@
                <table id="dataTable" class="table table-bordered table-striped" ;>
                    <thead>
                        <tr>
                            <th style="width: 90px;">Meeting ID</th>
                            <th style="width: 105px;">Agenda Item</th>
                            <th style="width: 85px;">Legistar ID</th>
                            <th>Title</th>
                        </tr>
                    </thead>
                </table>


            </div>
        </div>
    </div>
</body>

Any help on this would be greatly appreciate it.

Thank you, Erasmo

Update

html mark-up

@* Modal Window *@
<div class="modal" id="myModal">
    <div class="modal-dialog">
        <div class="modal-content">
            <div class="modal-header">
                <h4 class="modal-title">Upload Common Council Data</h4>
                <button type="button" class="close" data-dismiss="modal">×</button>
            </div>
            <div class="modal-body">
                <div class="col-md-12">
                    <div class="input-group">
                        <div class="custom-file">
                            <input type="file" name="file" class="custom-file-input" onchange="angular.element(this).scope().UploadFile(this.files)" />
                            <label class="custom-file-label" for="inputGroupFile04">Choose file</label>
                        </div>
                        <div class="input-group-append">
                            <button class="btn btn-outline-secondary" type="button" ng-disabled="!SelectedFileForUpload" ng-click="ParseExcelDataAndSave()" accept=".csv, application/vnd.openxmlformats-officedocument.spreadsheetml.sheet, application/vnd.ms-excel" />><i class="fa fa-upload"></i> Upload</button>
                        </div>
                    </div>
                    <span class="text-success">
                        {{Message}}
                    </span>
                </div>
            </div>
            <div class="modal-footer">
                <button type="button" class="btn btn-danger rounded-0" data-dismiss="modal" ng-click="clearText()">Close</button>
            </div>
        </div>
    </div>
</div>

screen shot - upload file windows

erasmo carlos
  • 780
  • 1
  • 10
  • 29
  • What do you mean by "Excel files?" Do you mean files that have a `.xlsx` extension? How about older Excel extensions like `.xls`? How about `.csv`? How about Excel files with non-standard extensions? If it's as simple as limiting the filename to several known extensions, you can just do that at the HTML level with the [pattern attribute](https://www.w3schools.com/tags/att_pattern.asp) on your `` tag. – kmoser May 09 '20 at 01:31
  • 1
    I can't see the `accept` attribute on your `input` in your updated question. Actually you should apply it to your `input` not your `button` – SMAKSS May 09 '20 at 03:15

3 Answers3

2

You can simply achieve this by adding accept attribute to your input like this:

<input type="file" accept=".csv" />

But if you want to be much more clear and accept all version of excel you should use this one instead:

<input type="file" accept=".csv, application/vnd.openxmlformats-officedocument.spreadsheetml.sheet, application/vnd.ms-excel" />

For Excel Files 97-2003 (.xls), use:

application/vnd.ms-excel

For Excel Files 2007+ (.xlsx), use:

application/vnd.openxmlformats-officedocument.spreadsheetml.sheet

Also, there is the same question in SO that you can find here.

SMAKSS
  • 7,423
  • 3
  • 9
  • 29
  • I tried this, but when I click the file upload button, I can still see all types of files, not just the aforementioned ones. – erasmo carlos May 09 '20 at 03:05
  • 1
    @erasmocarlos Check the live demo [here](http://jsfiddle.net/dirtyd77/LzLcZ/144/). If you stick with the first input you will only see the `.csv` extension files. – SMAKSS May 09 '20 at 03:09
  • I think the issue is that (if you look at my markup, I am using a button, not an input element – erasmo carlos May 09 '20 at 03:21
  • 1
    @erasmocarlos You have an input like this: `` so you should apply the `accept` attribute to this one not to your `button` element. It won't work on buttons. Just try it and see the results. – SMAKSS May 09 '20 at 03:23
  • 1
    @erasmocarlos Keep in mind, the button is for illustration only, the main work is on `input` with type `file` so that is the element who work for uploading here. You can read [this](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/file) for more information. – SMAKSS May 09 '20 at 03:26
  • 1
    Totally my fault. I was adding the accept to the wrong element. My apologies. It works now. Thank you for your help, and patience explaining. – erasmo carlos May 09 '20 at 03:26
2
<input type="file" accept=".xlsx, .xls, .csv"/>
Umair Latif
  • 388
  • 2
  • 6
  • I tried this, but when I click the file upload button, I can still see all types of files, not just the aforementioned ones. – erasmo carlos May 09 '20 at 03:05
2

If you want something safe, check the magic numbers in the first n bytes of your file.

read Magic Numbers

something like this:

const isXlsFile = (chunk) => {
    const types = [
        {
            ext: 'xls',
            mime: 'application/vnd.ms-excel',
            // magic numbers
            bytes: new Uint8Array([0x09, 0x08, 0x10, 0x00, 0x00, 0x06, 0x05, 0x00])
        },
        {
            ext: 'xlsx',
            mime: 'application/vnd.ms-excel',
            bytes: new Uint8Array([0x50, 0x4B, 0x03, 0x04, 0x14, 0x00, 0x06, 0x00])
        }
    ]

    for (const type of types) {
        if (chunk.indexOf(type.bytes) === 0) return type;
    }

    return false;
};

this is just to check, then if it matches your type you proceed to save the file

max
  • 449
  • 2
  • 9