Đọc nội dung file trên máy client để hiển thị ảnh hoặc upload bằng ajax

Đọc nội dung file trên máy client để hiển thị ảnh hoặc upload bằng ajax

Trong bài viết này tôi sẽ trình bày hướng tiếp cận đọc nội dung file nằm trên máy của client bằng file API của HTML5. Trước đây có khá nhiều kỹ thuật giúp đọc file tại local như dùng Flash, ActiveX, dùng Frame để upload file lên server, sử dụng đường dẫn dạng file:/// để gán vào src của img… nhưng các kỹ thuật này đều có hạn chế là cần plugin, lập trình server-side hoặc không được hỗ trợ vì lý do bảo mật. Trong tương lai gần HTML5 sẽ là giải tối ưu cho vấn đề này khi hầu hết các trình duyệt đều hỗ trợ (Chrome, Firefox, Safari, Opera).
 

File API

File API của HTML5 cho phép ta tương tác với FileSystem của client. Người dùng sau khi chọn một (hoặc nhiều) file là ta có thể dùng javascript: phương thức readAsDataURL lớp FileReader để đọc nội dung. Để làm mẫu một ví dụ, bạn chuẩn bị 1 input type=”file” và một textarea để hiển thị kết quả như sau:

<script type="text/javascript">
function file_change(f){
	var reader = new FileReader();
	reader.onload = function (e) {
		document.getElementById("output").value = e.target.result;
	};
	reader.readAsDataURL(f.files[0]);
}
</script>
<input type="file" onchange="file_change(this)" />
<textarea id="output"></textarea>

Khi tôi chọn một file text ở local có nội dung bên trong là: http://tek.eten.vn thì kết quả là: data:text/plain;base64,aHR0cDovL3Rlay5ldGVuLnZu. Xem demo: DEMO
 

Sự hỗ trợ file api trên các trình duyệt phổ biến

IE FF GC Safari Opera iOS Safari Opera mini Android browser BB browser Opera mobile GC for Android FF for Android
3 ver trước 6 13 18 4.0 11.1 4.0-4.1 2.3 11.0
2 ver trước 7 14 19 5.0 11.5 4.2-4.3 3.0 11.1
ver trước 8 15 20 5.1 11.6 5.0-5.1 4.0 11.5
Hiện tại 9 16 21 6.0 12.0 6.0 5.0-7.0 4.1 7.0 12.0 18.0 15
Sắp tới 10 17 22 12.1 10.0 12.1
Tương lai 18 23 12.5

Không hỗ trợ Hỗ trợ một phần Hỗ trợ
IE: Internet Explorer
FF: Firefox
GC: Google Chrome
BB: Blackberry
ver: Version
 

Làm ảnh xem trước các ảnh ở client do người dùng chọn

Với ví dụ trên tôi chỉ cần thay đổi một chút sẽ giúp hiển thị ảnh ở client mà không cần upload lên server:

<script type="text/javascript">
function file_change(f){
	var reader = new FileReader();
	reader.onload = function (e) {
		var img = document.getElementById("img");
		img.src = e.target.result;
		img.style.display = "inline";
	};
	reader.readAsDataURL(f.files[0]);
}
</script>
<input id="f" type="file" onchange="file_change(this)" style="display: none" /><input type="button" value="Chọn ảnh" onclick="document.getElementById('f').click()" />
<img id="img" style="display: none" />

Lưu ý bạn có thể kiểm tra đuôi file để quyết định xem đó có phải là ảnh hay không bằng cách lấy tên file trong f.value hoặc kiểm tra xem content type trong e.target.result có phải là images/* hay không. Xem demo: DEMO
 

Upload file bằng ajax

Nếu bạn để ý thì nội dung file khi browser đọc được sẽ trả lại cho ta dưới dạng mã hóa base64. Ta hoàn toàn có thể lấy nội dung này và sử dụng ajax để upload lên server mà không cần submit trang hiện tại. Để tạo hiệu ứng, tính toán tốc độ upload, bạn thực hiện bằng thay vì gọi 1 lần ajax để truyền toàn bộ nội dung file, hãy chia nó thành nhiều phần. Phía server side đọc từng mảng và sau cho chỉ việc ghép lại và giải mã base64 để trở thành mảng byte để lưu trữ lại file gốc. Do chia nhỏ thành nhiều gói tin nên bạn dễ dàng hủy bỏ tiến trình upload này.
 
Tôi đã viết chức năng này theo cách thiết kế lớp trong javascript thành thư viện vn.eten.web.AjaxUploader.
Hàm khởi tạo nhận vào tham số option gồm các thông số:

  • file: biến HTML DOM là tham chiếu đến input type=file
  • base: Đường dẫn đến thư mục chứa trang xử lý upload – mặc định là “”
  • service: Tên trang xử lý upload – mặc định là “UploadSvr.ashx”
  • bufferSize: kích thước dữ liệu của mỗi lần upload – mặc định là 64 * 1024 byte (= 64kB)
  • start: hàm callback được gọi khi phiên upload được tạo
  • chunkDone: hàm callback được gọi khi mỗi lần upload một gói tin thành công
  • done: hàm callback được gọi khi upload toàn bộ xong
  • fail: hàm callback được gọi khi upload thất bại
  • finalize: hàm callback được gọi khi upload xong chunk cuối cùng và chờ server xử lý
  • cancel: hàm callback được gọi khi hủy xong tiến trình upload

 
Lớp vn.eten.web.AjaxUploader có 2 phương thức không đối số là start() để bắt đầu phiên upload và isSupported() để kiểm tra xem browser có hỗ trợ hay không.
 
Server side hỗ trợ như sau:

  • Tạo phiên upload: UploadSvr.ashx?cmd=create trả về: OK key nếu thành công
  • Upload từng gói tin: UploadSvr.ashx và POST: k=key data=data_base64 trả về: OK nếu thành công
  • Hủy upload: UploadSvr.ashx?cmd=cancel và POST: k=key trả về OK nếu thành công
  • Hoàn thành upload: UploadSvr.ashx?cmd=close và POST: k=key trả về OK nếu thành công

Bạn có thể xem demo tại đây.

Khi trích dẫn bài viết từ tek.eten.vn, xin vui lòng ghi rõ nguồn. Chúng tôi sẽ rất cảm ơn bạn!