0
votes

I want to iterate line by line over a user uploaded file in my Angular app. I have tried the approach stated in this answer but I get the following error:

core.js:6260 ERROR TypeError: this.firstfile.split is not a function or its return value is not iterable at AppComponent.firstfileupload (app.component.ts:23) at AppComponent_Template_input_change_2_listener (app.component.html:2) at executeListenerWithErrorHandling (core.js:21815) at wrapListenerIn_markDirtyAndPreventDefault (core.js:21857) at HTMLInputElement. (platform-browser.js:976) at ZoneDelegate.invokeTask (zone-evergreen.js:399) at Object.onInvokeTask (core.js:41640) at ZoneDelegate.invokeTask (zone-evergreen.js:398) at Zone.runTask (zone-evergreen.js:167) at ZoneTask.invokeTask [as invoke] (zone-evergreen.js:480)

My code for app.component.ts

import { Component } from '@angular/core';
import { HttpClient } from '@angular/common/http';


@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  firstfile=null;
  second_file = null;
  title = 'first';

  constructor(private http:HttpClient){

  }

  firstfileupload(event){
    console.log("First file")
    console.log(event)
    this.firstfile=event.target.files[0]
    for(const line of this.firstfile.split(/[\r\n]+/)){
      console.log(line)
    }
    console.log("First file File has been changed")
  }
  secondfile(event){
    this.second_file=event.target.files[0];
    // for(const line of this.second_file.split(/[\r\n]+/)){
    //   console.log(line)
    // }
    console.log("Second file uploaded")
  }
  onUpload(){
    console.log("Upload button clicked")
    // const fd = new FormData();
    // fd.append('files',this.firstfile);
    // fd.append('files',this.second_file);
    // this.http.post('http://localhost:5000',fd).subscribe(res =>{
    //   console.log(res)
    // }

    // )
  }
}

And for app.component.html

<h1>Upload the files</h1>
<input type="file" (change)="firstfileupload($event)">
<input type="file" (change)="secondfile($event)">
<button type="button" (click)="onUpload()">Upload</button>

How can I iterate over an uploaded file ? I would rather not save the file and just iterate there only. Thanks in advance.

2
Does this answer your question? Angular - Read a file and parse its contentHeretic Monkey
My question is a bit different. Thanks for feedbackSaurav Ahlawat
The answer is exactly the same though....Heretic Monkey
I think the answer here gives much more clarity and better implementation, than the one over there. However, if you still want to close this, you can.Saurav Ahlawat

2 Answers

0
votes

You problem, based on this.firstfile.split is not a function or its return value is not iterable error, is that you are trying to iterate an object (i.e., the file), not its contents.

firstfileupload(event){
    . . .
    this.firstfile=event.target.files[0]  // <<<----- You recover the file. As OBJECT
    for(const line of this.firstfile.split(/[\r\n]+/)){ // <<<--- You want to iterate the OBJECT like it is an STRING element
      . . .
    }
    . . .
  }

You need to use the FileReader helper to iterate file contents.

Check this: Angular - Read a file and parse its content

0
votes

You could try to read the contents of the file using FileReader.readAsText(). Try the following

import { Component } from '@angular/core';
import { HttpClient } from '@angular/common/http';

import { Observable, Subject } from 'rxjs';

export class AppComponent {
  ...

  firstfileupload(event) {
    this.firstfile = event.currentTarget.files[0];

    this.readFileContent(event.currentTarget.files[0]).subscribe(
      content => {
        for(const line of content.split(/[\r\n]+/)) {
          if (line !== '') {          // <-- regex pattern might return an empty line at the EOF
            console.log(line);
          }
        }
      }
    );

    console.log("First file File has been changed")
  }

  private readFileContent(file): Observable<any> {
    let result = new Subject<any>();           // <-- Step 1: Create an empty RxJS `Subject`

    const reader = new FileReader();           // <-- Step 2: Create a `FileReader` object
    reader.onload = (e) => {                   // <-- Step 5: Callback function to trigger on the evnet `onload`
      const fileContent = e.target.result;     // <-- Step 6: `event.target.result` contains the file content
      result.next(fileContent);                // <-- Step 7: Push the file contents to the `result` subject
    };
    reader.readAsText(file);                   // <-- Step 3: Read the file using `FileReader`'s method `readAsText`

    return result.asObservable();              // <-- Step 4: Return the `result` subject
  }
}

The FileReader is part of the File API. It contains the following events that are triggered based on their respective actions.

+------------+----------------------------------------------------------------+
| Event name | Fired when…                                                    |
+------------+----------------------------------------------------------------+
| loadstart  | When the read starts.                                          |
| progress   | While reading (and decoding) blob                              |
| abort      | When the read has been aborted.                                |
|            | For instance, by invoking the abort() method.                  |
| error      | When the read has failed (see file read errors).               |
| load       | When the read has successfully completed.                      |
| loadend    | When the request has completed (either in success or failure). |
+------------+----------------------------------------------------------------+

We are creating the object and reading the contents of the file using the callback function to the load event. Since the data handling is asynchronous, we use a RxJS Subject to read the data returned from the function readFileContent asynchronously.

You could learn more about asynchronous data here: https://stackoverflow.com/a/14220323/6513921