การใช้งาน Progress Event กับการอัพโหลดไฟล์ ใน HttpClient ตอนที่ 6

เขียนเมื่อ 6 ปีก่อน โดย Ninenik Narkdee
httpclient progress event angular

คำสั่ง การ กำหนด รูปแบบ ตัวอย่าง เทคนิค ลูกเล่น การประยุกต์ การใช้งาน เกี่ยวกับ httpclient progress event angular

ดูแล้ว 7,717 ครั้ง


เนื้อหาในตอนที่ 6 นี้เราจะมาดูในเรื่องการใช้งาน Advance HttpClient ใน Anuglar ส่วนของ
การใช้งาน progress event กรณีที่ใช้งานร่วมกับการอัพโหลดไฟล์ที่มีขนาดใหญ่ และเราต้องการ
ที่จะแสดงสถานะหรือความคืบหน้าของการอัพโหลด เช่นว่า ขณะนี้อัพโหลดไปแล้วคิดเป็นกี่เปอร์เซ็น
เป็นต้น
    ในตัวอย่างการประยุกต์และการใช้งานที่จะนำเสนอต่อไปนี้ จะมีด้วยกัน 2 รูปแบบ แบบแรกคือการเลือก
เฉพาะไฟล์สำหรับอัพโหลด โดยไม่มีการส่งข้อมูลใดๆ นอกจากไฟล์ที่ต้องการอัพโหลด และมีการใช้งาน
progress event หรือการใช้งาน event ที่แสดงถึงความคืบหน้าของอัพโหลดไฟล์ ส่วนแบบที่สองเราจะเป็น
การใช้งานกรณีส่งข้อมูลรูปภาพไปพร้อมกับข้อมูลของ element form อื่นเพิ่มเติมไปด้วย โดยในที่นี้เราจะ
ส่งข้อมูลจาก input text และก็ไฟล์อัพโหลดไปใช้งานพร้อมกัน โดยวิธีที่สองนี้จะไม่มีการใช้งาน progress event
    สำหรับในตัวอย่าง เราจะใช้รูปแบบของ input file ของ bootstrap 4 โดยจะยกตัวอย่างเป็นการอัพโหลด
เป็นไฟล์รูปแบบ มีการแสดง preview รูปภาพที่กำลังจะอัพโหลดด้วย
 

การอัพโหลดไฟล์ร่วมกับ การใช้งาน Progress Event

เริ่มต้นกันที่ไฟล์ register.component.css กำหนด css class สำหรับใช้งานดังนี้
 
ไฟล์ register.component.css
 
#place_previewImg{
    margin-bottom: 10px;
}
ไฟล์ register.component.html
 
<p>  register works!</p>
<pre>{{ responseValue | json }}</pre>
<pre>{{ registForm.value | json }}</pre>
<form (ngSubmit)="onSubmit(registForm)" #registForm="ngForm" novalidate>
  <div class="form-group row">
    <label for="file2" class="col-3 col-form-label text-right">
        อัพโหลดไฟล์
    </label>
    <div class="col-6">
      <label class="custom-file">
        <input (change)="onChangePic($event)" type="file" name="file2" id="file2" class="custom-file-input" accept="image/*">
        <span class="custom-file-control">{{ chooseFile }}</span>
      </label>          
    </div>
    <div class="offset-3"></div>
  </div>  
  <div class="form-row">
    <div class="col-9 offset-3">
      <img id="place_previewImg" *ngIf="previewLoaded" [src]="previewImg" style="height:150px;">
    </div>
  </div>     
  <div class="form-row">
    <div class="col-9 offset-3">
      <button type="submit" [disabled]="!registForm.valid" class="btn btn-primary">สมัครสมาชิก</button>
    </div>
  </div>    
</form>
<br>
 
ประยุกต์จากฟอร์มของบทความก่อนหน้า เราจะมีแค่ input type file สำหรับอัพโหลไฟล์รุปภาพ 
 
<input (change)="onChangePic($event)" type="file" name="file2" id="file2" class="custom-file-input" accept="image/*">
 
มีการกำหนด onChange event เข้าไปสำหรับทำการแสดงรูปภาพ preview ผ่านฟังก์ชั่น onChangePic($event) 
โดยส่งค่า Event Object เข้าไปในฟังก์ชั่นด้วย เมื่อมีการเลือกไฟล์รูปภาพ ก็จะให้แสดงชื่อไฟล์รูปภาพผ่านตัวแปร 
chooseFile
 
<span class="custom-file-control">{{ chooseFile }}</span>
 
และแสดงรูป prview ในส่วนของแท็ก img
 
<img id="place_previewImg" *ngIf="previewLoaded" [src]="previewImg" style="height:150px;">
 
หน้าตาของส่วนของฟอร์มอัพโหลดไฟล์ จะได้เป็นดังนี้
 
 


 
 
ตัวอย่างเมื่อเลือกไฟล์ (กรณีเพิ่มโค้ด javascript แล้ว)
 
 


 
 
ต่อไปดูในส่วนของไฟล์การทำงานเพื่อทำการอัพโหลดไฟล์ให้เราปรับไฟล์ register.component.ts เป็นดังนี้
 
ไฟล์ register.component.ts
 
import { Component, OnInit } from '@angular/core';
import { Router, ActivatedRoute, ParamMap } from '@angular/router';
import { HttpClient, HttpHeaders, 
        HttpErrorResponse, 
        HttpRequest,
        HttpEventType,
        HttpResponse,
        HttpParams } from '@angular/common/http';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/map';

@Component({
  // selector: 'app-register',
  templateUrl: './register.component.html',
  styleUrls: ['./register.component.css']
})
export class RegisterComponent implements OnInit {
  public provinces:string[] = ['กรุงเทพฯ','นนทบุรี'];
  public hobbies:string[] = ['ออกกำลังกาย','อ่านหนังสือ'];
  public urlApi:string = "http://localhost/demo/";
  public responseValue:any;  
  public chooseFile:string = '';
  public previewImg:any;
  public previewLoaded:boolean = false;
  public fileList:any[];
  public fileupload:any;

  constructor(
    private http:HttpClient,
    private route: ActivatedRoute,
    private router:Router
  ) { }  

  onSubmit(f:any){
    let data = f.value; // ในที่นี้ค่านี้ยังไม่ใช้ เพราะเราจะส่งแค่ไฟล์ไปอย่างเดียว

    let file:any = this.fileupload; // กำหนด file Object mี่จะทำการอัพโหลด
    const req = new HttpRequest('POST',this.urlApi+'show_data.php', file, {
      reportProgress: true, // กำหนด reportProgress เพื่อใ้ช้งาน prgress event
    });    

    this.http.request(req).subscribe(event => {
      // โดยรูปแบบการอัพโหลดด้วยวิธีนี้จะเป็นการส่งข้อมูลแบบ event stream 
      // ที่เราสามารถดูความคืบหน้าการอัพโหลดได้ 
      if (event.type === HttpEventType.UploadProgress) { // ขณะกำลังอัพโหลด
        // ทดสอบแสดงสถานะการอัพโหลดผ่่านทาง console
        const percentDone = Math.round(100 * event.loaded / event.total);
        console.log(`File is ${percentDone}% uploaded.`);
      } else if (event instanceof HttpResponse) {
        // แสดงสถานะอัพโหลดสำเร็จแล้วทาง console
        console.log('File is completely uploaded!');
        this.fileupload = null;
        this.chooseFile = '';
        this.previewLoaded = false;		
      }
    });    

  // การตรวจสอบการเลือกไฟล์และแสดงชื่อไฟล์
  onChangePic(e:any){
    console.log(e);
    let file = e.dataTransfer ? e.dataTransfer.files[0] : e.target.files[0];
    this.fileupload = file;
    this.fileList = e.target.files;
    console.log(file);
    console.log(typeof file);
    if(typeof(file)=='undefined'){
      console.log("Please choose file");
      this.chooseFile = '';
      this.previewImg = '';
      this.previewLoaded = false;
    }else{
      this.chooseFile = file.name;  
      this.previewThumbnail(file);
    }
  }
 
  // ส่วนของการแสดงรูป preview 
  previewThumbnail(file:any){
      let imageType = /image.*/
      if(!file.type.match(imageType)){
        alert('invalid format');
        return;
      }
      let reader = new FileReader();
      reader.onload = this._handleReaderLoaded.bind(this);
      reader.readAsDataURL(file);
  }

  // ส่วนของการส่ง dataUti ของรุปไปแสดงเป็น preview
  _handleReaderLoaded(e) {
    this.previewImg = e.target.result;
    this.previewLoaded = true;
  }


  ngOnInit() {

  }

}
 
 
 
คำอธิบายแสดงในโค้ด เรามีการกำหนด option ให้มีการใช้งาน progress event ผ่านการกำหนดค่า reportProgress: true
ผลลัพธ์ของการทำงานของโค้ดข้างต้นดังแสดงในรุปตัวด้านล่าง
 
 


 
 
ต่อไปส่วนของฝั่ง server หรือ backend service ที่เราทำการรับค่าและบันทึกไฟล์ที่ได้ทำการอัพโหลด
ในตัวอย่างเราส่งไปที่ไฟล์ show_data.php ให้เราปรับในส่วนของไฟล์นี้เป็นดังนี้
 
ไฟล์ show_data.php ไฟล์รับค่าฝั่ง server
 
<?php
header('Access-Control-Allow-Origin: *'); 
header('Access-Control-Allow-Headers: Content-Type,X-Custom-Header'); 
header('Content-type: application/json');
$jsonData = array();	
if($_SERVER['REQUEST_METHOD'] == "GET") {
	echo json_encode($jsonData);
}elseif($_SERVER['REQUEST_METHOD'] == "OPTIONS"){
	echo json_encode($jsonData);
}elseif($_SERVER['REQUEST_METHOD'] == "POST"){
	$uploadData = file_get_contents('php://input');
	//$vars=parse_str($res,$post_vars);
	$headers = getallheaders();	
	// https://www.freeformatter.com/mime-types-list.html
	$mime_types = array(
		'png' => 'image/png',
		'jpg' => 'image/jpeg',
		'jpeg' => 'image/jpeg',
		'jpg' => 'image/jpeg',
		'gif' => 'image/gif',
		'bmp' => 'image/bmp',
		'ico' => 'image/vnd.microsoft.icon',
		'tiff' => 'image/tiff',
		'tif' => 'image/tiff',
		'svg' => 'image/svg+xml',
		'svgz' => 'image/svg+xml',
	);
	$ext = array_search($headers['Content-Type'], $mime_types); 
	$filename = time().".".$ext;
	file_put_contents("upload/".$filename,$uploadData);
	echo json_encode(array(
		"statusCode"=>"200 OK",
		"statusMessage"=>"Successful"
	));	
}else{
    header("HTTP/1.1 403 Access Forbidden");
	echo json_encode(array(
		"statusCode"=>"403 Access Forbidden",
		"statusMessage"=>"Fail"
	));
}
?>
 
 
ทดสอบการทำงาน สังเกตจากรูปด้านล่าง เมื่อมีการอัพโหลดไฟล์เรียบร้อยแล้ว เราทำการล้างค่าข้อมูลเก่า
ในฟอร์มจะไม่มีการแสดงรุปภาพ preview แล้ว พร้อมสำหรับการอัพโหลดไฟล์ใหม่แล้ว ส่วนของ console
เราจะเห็นว่ามีการแสดง progress event ค่าเป็นเปอร์เซ็นต์การทำงานในตัวอย่างจะเห็นมี 72% แลถก็ 100%
เมื่อมีการอัพโหลดไฟล์สำเร็จ 
 
 


 
 
ตัวอย่างไฟล์รุปภาพที่อัพโหลดสำเร็จแล้ว
 
 

 
 
เป็นอันเสร็จเรียบร้อยสำหรับแนวทางแรกที่มีการใช้งาน progress event
 


 

การอัพโหลดไฟล์พร้อมกับข้อมูลอื่นๆในฟอร์มด้วย FormData Object

ไปต่อที่รูปแบบที่สอง ที่มีการส่งรูปภาพไปพร้อมกับข้อมูลฟอร์มไปบันทึกพร้อมกัน ให้เราปรับไฟล์ 
register.component.html โดยเพิ่ม input type text เข้าไปดังนี้
 
ไฟล์ register.component.html
 
<p>  register works!</p>
<pre>{{ responseValue | json }}</pre>
<pre>{{ registForm.value | json }}</pre>
<form (ngSubmit)="onSubmit(registForm)" #registForm="ngForm" novalidate>
  <div class="form-group row">
    <label for="fullname" class="col-3 col-form-label text-right">
        ชื่อ นามสกุล
    </label>
    <div class="col-6">
      <input type="text" class="form-control is-invalid" ngModel #fullname="ngModel" name="fullname" id="fullname" 
      [class.is-valid]="fullname.valid" [class.is-invalid]="!fullname.valid" 
      placeholder="Fullname" required>
      <div class="invalid-feedback">
        Please provide a valid Fullname.
      </div>      
    </div>
    <div class="offset-3"></div>
  </div>  

  <div class="form-group row">
    <label for="file2" class="col-3 col-form-label text-right">
        อัพโหลดไฟล์
    </label>
    <div class="col-6">
      <label class="custom-file">
        <input (change)="onChangePic($event)" type="file" name="file2" id="file2" class="custom-file-input" accept="image/*">
        <span class="custom-file-control">{{ chooseFile }}</span>
      </label>          
    </div>
    <div class="offset-3"></div>
  </div>  
  <div class="form-row">
    <div class="col-9 offset-3">
      <img id="place_previewImg" *ngIf="previewLoaded" [src]="previewImg" style="height:150px;">
    </div>
  </div>     
  <div class="form-row">
    <div class="col-9 offset-3">
      <button type="submit" [disabled]="!registForm.valid" class="btn btn-primary">สมัครสมาชิก</button>
    </div>
  </div>    
</form>
<br>
จากนั้นปรับโค้ดในส่วนของไฟล์ register.component.ts เป็นดังนี้
 
ไฟล์ register.component.ts
 
import { Component, OnInit } from '@angular/core';
import { Router, ActivatedRoute, ParamMap } from '@angular/router';
import { HttpClient, HttpHeaders, 
        HttpErrorResponse, 
        HttpRequest,
        HttpEventType,
        HttpResponse,
        HttpParams } from '@angular/common/http';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/map';

@Component({
  // selector: 'app-register',
  templateUrl: './register.component.html',
  styleUrls: ['./register.component.css']
})
export class RegisterComponent implements OnInit {
  public provinces:string[] = ['กรุงเทพฯ','นนทบุรี'];
  public hobbies:string[] = ['ออกกำลังกาย','อ่านหนังสือ'];
  public urlApi:string = "http://localhost/demo/";
  public responseValue:any;  
  public chooseFile:string = '';
  public previewImg:any;
  public previewLoaded:boolean = false;
  public fileList:any[];
  public fileupload:any;

  constructor(
    private http:HttpClient,
    private route: ActivatedRoute,
    private router:Router
  ) { }  

  onSubmit(f:any){
    let data = f.value; 
    let file:any = this.fileupload;
    let formData:FormData = new FormData();
    
    // กรณีมีการเลือกไฟล์ ส่งไฟล์ไปใช้งานกับ formData
    if(file && file!='undefined'){
      console.log(file);
      formData.append('uploadFile', file, file.name);
    }
    // วนลูปค่าจาก form ที่ส่งเข้ามา ไปใช้งานใน formData
    for(let key in data ) {
      formData.append(key, data[key]);
    }    

    this.http.post(this.urlApi+'show_data.php',formData,{
      headers: new HttpHeaders().set('X-Custom-Header', 'my-header'),
      params: new HttpParams().set('id', '3')
    })    
    .subscribe(result =>{
      this.responseValue = result;
      console.log(result);
      f.reset(); // reset form
      this.fileupload = null;
      this.chooseFile = '';
      this.previewLoaded = false;      
    },
    ( err:HttpErrorResponse ) => {
      // กรณี error
      if (err.error instanceof Error) {
        // กรณี error ฝั่งผู้ใช้งาน หรือ การเชื่อมต่อเกิด error ขึ้น
        console.log('An error occurred:', err.error.message);
      }else{ // กรณี error ฝั่ง server ไม่พบไฟล์ ,server error 
        console.log(`Backend returned code ${err.status}, body was: ${err.error}`);
      }       
    });
  }

  // การตรวจสอบการเลือกไฟล์และแสดงชื่อไฟล์
  onChangePic(e:any){
    console.log(e);
    let file = e.dataTransfer ? e.dataTransfer.files[0] : e.target.files[0];
    this.fileupload = file;
    this.fileList = e.target.files;
    console.log(file);
    console.log(typeof file);
    if(typeof(file)=='undefined'){
      console.log("Please choose file");
      this.chooseFile = '';
      this.previewImg = '';
      this.previewLoaded = false;
    }else{
      this.chooseFile = file.name;  
      this.previewThumbnail(file);
    }
  }
 
  // ส่วนของการแสดงรูป preview 
  previewThumbnail(file:any){
      let imageType = /image.*/
      if(!file.type.match(imageType)){
        alert('invalid format');
        return;
      }
      let reader = new FileReader();
      reader.onload = this._handleReaderLoaded.bind(this);
      reader.readAsDataURL(file);
  }

  // ส่วนของการส่ง dataUti ของรุปไปแสดงเป็น preview
  _handleReaderLoaded(e) {
    this.previewImg = e.target.result;
    this.previewLoaded = true;
  }


  ngOnInit() {

  }

}
 
 
สำหรับการใช้งาน formData นั้นค่าที่ส่งไป จะเหมือนกับการส่งค่าปกติ ในตัวอย่างเราส่งข้อมูลแบบ POST เวลารับค่า
ในไฟล์ฝั่ง server เราก็รับค่าด้วยตัวแปร $_POST กรณีส่งค่าแบบ GET ก็รับค่าผ่านตัวแปร $_GET ส่วนถ้าเป็นไฟล์ส่งไป
ก็รับค่าผ่านตัวแปร $_FILSES เป็นรูปแบบการใช้งานปกติ ในที่นี้จะไม่ขอยกตัวอย่างโค้ดการอัพโหลดไฟล์ สามารถค้นหา
เพิ่มเดิมในเว็บไซต์ได้ จะแสดงแค่ค่าที่ถูกส่งไปดังนี้
 
ไฟล์ show_data.php
 
<?php
header('Access-Control-Allow-Origin: *'); 
header('Access-Control-Allow-Headers: Content-Type,X-Custom-Header'); 
header('Content-type: application/json');
$jsonData = array();	
if($_SERVER['REQUEST_METHOD'] == "GET") {
	echo json_encode($jsonData);
}elseif($_SERVER['REQUEST_METHOD'] == "OPTIONS"){
	echo json_encode($jsonData);
}elseif($_SERVER['REQUEST_METHOD'] == "POST"){
	echo json_encode(array(
		"statusCode"=>"200 OK",
		"statusMessage"=>"Successful",
		"getData"=>$_GET,
		"fileUpload"=>$_FILES,
		"postData"=>$_POST
	));
}else{
    header("HTTP/1.1 403 Access Forbidden");
	echo json_encode(array(
		"statusCode"=>"403 Access Forbidden",
		"statusMessage"=>"Fail"
	));
}
?>
 
ทดสอบการทำงาน กรอกข้อมูล เลือกรูปภาพ จากนั้นกด submit
 
 


 
 
จะได้ค่าของข้อมูลที่ถูกส่งไปดังนี้

 
 


 
 
สามารถนำไปประยุกต์เพิ่มเติมได้ตามต้องการ 
ตอนนี้เราได้ทำความเข้าใจในการรับส่งข้อมูลระหว่าง front-end และ backend ไปมากพอสมควรแล้ว เนื้อหาต่อไป 
จะเป็นอะไร รอติดตาม 
 


กด Like หรือ Share เป็นกำลังใจ ให้มีบทความใหม่ๆ เรื่อยๆ น่ะครับ



อ่านต่อที่บทความ









เนื้อหาที่เกี่ยวข้อง









URL สำหรับอ้างอิง





คำแนะนำ และการใช้งาน

สมาชิก กรุณา ล็อกอินเข้าระบบ เพื่อตั้งคำถามใหม่ หรือ ตอบคำถาม สมาชิกใหม่ สมัครสมาชิกได้ที่ สมัครสมาชิก


  • ถาม-ตอบ กรุณา ล็อกอินเข้าระบบ
  • เปลี่ยน


    ( หรือ เข้าใช้งานผ่าน Social Login )







เว็บไซต์ของเราให้บริการเนื้อหาบทความสำหรับนักพัฒนา โดยพึ่งพารายได้เล็กน้อยจากการแสดงโฆษณา โปรดสนับสนุนเว็บไซต์ของเราด้วยการปิดการใช้งานตัวปิดกั้นโฆษณา (Disable Ads Blocker) ขอบคุณครับ