Đăng tải Files với AJAX
() translation by (you can also view the original English article)
Dườn như tôi không thể tìm thấy điểm dừng với những thứ vui thú mà bạn có thể làm qua những công nghệ web đang trở nên nổi bật kia. Hôm nay, tôi sẽ cho bạn xem cách thực hiện vài thứ - cho đến lúc này - hầu như chưa từng được biết đến, đăng tải file với AJAX.
Chắc chắn có nhiều mánh khoé, nhưng nếu bạn giống tôi, và cảm thấy bất ổn mỗi khi bạn gõ iframe
, thì bạn sẽ thích thú điều này lắm đây.
Tìm kiếm một giải pháp nhanh chóng?
Nếu bạn đang tìm một giải pháp nhanh chóng, thì có một bộ sưu tập tuyệt vời của việc "các ứng dụng và mã lệnh cho việc upload file" trên Envato Market.
HTML5 file uploader đặc biệt tiện lợi - bạn có thể thêm file đễ dàng bằng cách kéo thả chúng hoặc click chọn. Tất cả file sẽ được upload qua AJAX hoặc có thể được bổ sung trong một form, và các file có thể được đặt tên lại trước khi đăng tải. Một giải pháp nhanh chóng, tuyệt vời nếu đó là điều bạn đang tìm kiếm!



Tại sao chúng ta không vượt qua những tin xấu?
Nó không hiệu quả với mọi trình duyệt. Tuy nhiên, với một vài cải thiện to lớn (hoặc bất cứ gì), thì chũng ta sẽ có một trang upload sẽ làm việc trên cả IE6 (dù không có AJAX bits).
Phần upload AJAX sẽ làm việc tốt khi
FormData
đang có sẵn, mặt khác, người dùng sẽ có một phần upload thông thưỡng.
Có ba thành phần chính trong dự án của chúng tôi.
- Thuộc tính
multiple
(nhiều) trong thành phầninput
. - Đối tượng
FileReader
từ File API mới. - Đối tượng
FormData
từ XMLHttpRequest2.
Chúng ta sẽ dùng thuộc tính multiple
để cho phép người dùng chọn nhiều file để upload (nhiều file upload sẽ hoạt động bình thường nếu FormDat
a không hiệu quả). Như bạn thấy, FileReader
cho phép chúng ta cho người dùng thấy hình đại diện của file chúng ta đang upload (chúng ta sẽ trông đợi hình ảnh).
Không có trong ba tính năng của chúng tôi hoạt động trong IE9, Cả ba tính năng sẽ không hoạt động được trên IE9, vì vậy tất cả người dùng IE sẽ có được trải nghiệm tải lên bình thường (mặc dù tôi hiểu sự hỗ trợ cho `FileReade
r` có sẵn trong IE10 Dev Preview 2). FileReader không hoạt động trong phiên bản mới nhất của Safari (5.1), vì vậy chúng sẽ không nhận được hình đại diện, nhưng chúng sẽ thực hiện được việc tải lên bằng AJAX và thông báo xác nhận. Cuối cùng, Opera 10.50 có hỗ trợ FileReade
r nhưng không hỗ trợ FormData
, vì thế họ sẽ có hình đại diên nhưng lại không có phần upload thông thường.
Với cách đó, hãy bắt tay viết code nào!
Bước 1: Markup và Style
Hãy bắt đầu với vài markup và style cơ bản. Dĩ nhiên, đây không phải phần trọng tâm của bài hướng dẫn, nên tôi sẽ không xem bạn là người mới bắt đầu đâu.
HTML
1 |
<!DOCTYPE html>
|
2 |
<html lang="en"> |
3 |
<head>
|
4 |
<meta charset="UTF-8" /> |
5 |
<title>HTML5 File API</title> |
6 |
<link rel="stylesheet" href="style.css" /> |
7 |
</head>
|
8 |
<body>
|
9 |
<div id="main"> |
10 |
<h1>Upload Your Images</h1> |
11 |
<form method="post" enctype="multipart/form-data" action="upload.php"> |
12 |
<input type="file" name="images" id="images" multiple /> |
13 |
<button type="submit" id="btn">Upload Files!</button> |
14 |
</form>
|
15 |
|
16 |
<div id="response"></div> |
17 |
<ul id="image-list"> |
18 |
|
19 |
</ul>
|
20 |
</div>
|
21 |
|
22 |
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.6.2/jquery.min.js"></script> |
23 |
<script src="upload.js"></script> |
24 |
</body>
|
25 |
</html>
|
Quá cơ bản phải không? Chúng ta đã có một form để đăng tải đến file upload.php
, chúng ta sẽ xem nó chốc nữa, và một thành phần input ở dạng file
. Chú ý rằng nó có nhiều
thuộc tính, cho phép người dùng chọn nhiều file cùng lúc.
Đó thực sự là tất cả cần xem ở đây. Tiếp tục nào.
CSS
1 |
body { |
2 |
font: 14px/1.5 helvetica-neue, helvetica, arial, san-serif; |
3 |
padding:10px; |
4 |
}
|
5 |
|
6 |
h1 { |
7 |
margin-top:0; |
8 |
}
|
9 |
|
10 |
#main { |
11 |
width: 300px; |
12 |
margin:auto; |
13 |
background: #ececec; |
14 |
padding: 20px; |
15 |
border: 1px solid #ccc; |
16 |
}
|
17 |
|
18 |
#image-list { |
19 |
list-style:none; |
20 |
margin:0; |
21 |
padding:0; |
22 |
}
|
23 |
#image-list li { |
24 |
background: #fff; |
25 |
border: 1px solid #ccc; |
26 |
text-align:center; |
27 |
padding:20px; |
28 |
margin-bottom:19px; |
29 |
}
|
30 |
#image-list li img { |
31 |
width: 258px; |
32 |
vertical-align: middle; |
33 |
border:1px solid #474747; |
34 |
}
|
Hoàn toàn không có gì đặc biệt ở đây.
Bước 2: PHP
Chúng ta cũng cần có thể xử lý phần upload file ở back-end, vì thế hãy tiếp tục.
upload.php
1 |
<?php
|
2 |
|
3 |
foreach ($_FILES["images"]["error"] as $key => $error) { |
4 |
if ($error == UPLOAD_ERR_OK) { |
5 |
$name = $_FILES["images"]["name"][$key]; |
6 |
move_uploaded_file( $_FILES["images"]["tmp_name"][$key], "uploads/" . $_FILES['images']['name'][$key]); |
7 |
}
|
8 |
}
|
9 |
|
10 |
echo "<h2>Successfully Uploaded Images</h2>"; |
Hãy nhớ rằng đây là những dòng PHP đầu tiên tôi đã viết trong một năm (tôi là một người chuyên về Ruby). Bạn nên thực nó theo một cách an toàn hơn; tuy nhiên, chúng ta đơn giản chỉ chắc rằng không có lỗi xảy ra khi upload. Nếu là trường hợp đó, chúng ta sẽ sử dụng hàm dựng sẵn move_uploaded_file
để di chuyển file đến một thư mục tên uploads
. Đừng quên bảo đảm rằng thư mục đó cho phép ghi file.
Vậy bây giờ chúng ta nên có một form upload đang hoạt động. Bạn chọn một ảnh (nhiều ảnh, nếu bạn muốn và trình duyệt của bạn cho phép), click vào nút "Upload Files!", và bạn sẽ nhận thông điệp "Successfully Uploaded Images" (Upload hình ảnh thành công).
Đây là dự án nhỏ của chúng ta cho đến giờ:



Nhưng xem nào, giờ năm 2011: chúng ta muốn nhiều hơn thế. Bạn chú ý rằng chúng ta đã liên kết với jQuery và một file upload.js
. Hãy mở nó ra nào.
Bước 3: JavaScript
Không phí thời gian nữa: thực hiện thôi!
1 |
(function () { |
2 |
var input = document.getElementById("images"), |
3 |
formdata = false; |
4 |
|
5 |
if (window.FormData) { |
6 |
formdata = new FormData(); |
7 |
document.getElementById("btn").style.display = "none"; |
8 |
} |
9 |
|
10 |
|
11 |
}(); |
Đây là những gì chúng ta bắt đầu. Chúng ta tạo ra hai biến: input
là phần tử nhập vào của chúng ta; formdat
a sẽ được sử dụng để gửi hình ảnh tới máy chủ nếu trình duyệt hỗ trợ. Chúng tôi khởi tạo nó với giá trị false
và sau đó kiểm tra để xem nếu trình duyệt hỗ trợ FormData
; Nếu có, chúng ta tạo một đối tượng FormData
mới. Ngoài ra, nếu chúng tôi có thể gửi hình ảnh bằng AJAX, chúng tôi không cần nút "Upload Images!", vì vậy chúng tôi có thể giấu nó đi. Tại sao chúng ta không cần nó? Vâng, chúng tôi sẽ tự động tải lên hình ảnh một cách kỳ diệu ngay lập tức sau khi người dùng chọn chúng.
Phần còn lại của JavaScript sẽ đi vào bên trong hàm ẩn danh tự kích hoạt của bạn. Tiếp theo chúng ta tạo ra một hàm helper nhỏ sẽ hiển thị hình ảnh khi trình duyệt có chúng:
1 |
function showUploadedItem (source) { |
2 |
var list = document.getElementById("image-list"), |
3 |
li = document.createElement("li"), |
4 |
img = document.createElement("img"); |
5 |
img.src = source; |
6 |
li.appendChild(img); |
7 |
list.appendChild(li); |
8 |
} |
Hàm này lấy một tham số: nguồn hình ảnh (chúng ta sẽ nhanh chóng thấy cách thực hiện nó). Sau đó, chúng tôi chỉ cần tìm danh sách trong markup của chúng tôi và tạo một danh sách và hình ảnh. Chúng tôi thiết lập nguồn hình ảnh đến nguồn chúng tôi nhận được, đưa hình ảnh vào item của danh sách, và đưa item vào danh sách.
Tiếp theo, chúng ta phải lấy các hình ảnh, hiển thị chúng và upload chúng. Như đã nói, chúng tôi sẽ thực hiện việc này khi sự kiện onchange
được kích hoạt trên phần tử input.
1 |
if (input.addEventListener) { |
2 |
input.addEventListener("change", function (evt) { |
3 |
var i = 0, len = this.files.length, img, reader, file; |
4 |
|
5 |
document.getElementById("response").innerHTML = "Uploading . . ." |
6 |
|
7 |
for ( ; i < len; i++ ) { |
8 |
file = this.files[i]; |
9 |
|
10 |
if (!!file.type.match(/image.*/)) { |
11 |
|
12 |
} |
13 |
} |
14 |
|
15 |
}, false); |
16 |
} |
Chúng tôi không phải lo lắng về mô hình sự kiện ưu tiên của IE, bởi vì IE9 + hỗ trợ hàm addEventListener chuẩn.
Có nhiều hơn, nhưng chúng ta hãy bắt đầu với điều này. Trước tiên, chúng ta không phải lo lắng về mô hình sự kiện độc quyền của IE, bởi vì IE9 trở lên hỗ trợ hàm addEventListener
chuẩn (và từ IE9 trở xuống không hỗ trợ các tính năng mới của chúng tôi).
Vì vậy, chúng ta muốn làm gì khi người dùng đã chọn các file? Đầu tiên, chúng ta tạo ra một vài biến. Biến quan trọng nhất bây giờ là len = this.files.length.
Các file mà người dùng đã chọn sẽ có thể truy cập được từ đối tượng this.files
. Ngay bây giờ, chúng ta chỉ quan tâm đến thuộc tính length
, vì vậy chúng ta có thể chạy vòng lặp trên các files ...
... đó là chính xác những gì chúng ta đang làm tiếp theo. Trong vòng lặp, chúng tôi thiết lập file
hiện tại để nó dễ dàng truy cập. Việc tiếp theo chúng ta làm là xác nhận rằng file này là một hình ảnh. Chúng ta có thể làm điều này bằng cách so sánh thuộc tính type
với regular expression. Chúng tôi đang tìm một type bắt đầu bằng "image". (The double-bang ở phía trước chỉ chuyển đổi kết quả thành một giá trị boolean.)
Vậy, chúng ta làm gì nếu chúng ta có một hình ảnh trong tay?
1 |
if ( window.FileReader ) { |
2 |
reader = new FileReader(); |
3 |
reader.onloadend = function (e) { |
4 |
showUploadedItem(e.target.result); |
5 |
}; |
6 |
reader.readAsDataURL(file); |
7 |
} |
8 |
if (formdata) { |
9 |
formdata.append("images[]", file); |
10 |
} |
Chúng ta kiểm tra xem trình duyệt có hỗ trợ tạo các đối tượng FileReader
không. Nếu nó có, chúng ta sẽ tạo một đối tượng.
Đây là cách chúng ta dùng đối tượng FileReader
: chúng ta sẽ đưa đối tượng file
truyền đến phương thức reader.readAsDataURL
. Nó tạo một data url cho hình ảnh được upload. Nó không hoạt động theo cách bạn mong đợi. Data url không được trả lại từ hàm này. Thay vào đó, data url sẽ là một phần của một đối tượng sự kiện.
Với ý nghĩ đó, chúng tôi sẽ cần phải đăng ký một hàm trên sự kiện reader.onloadend
. Hàm này lấy một đối tượng sự kiện, lúc đó chúng ta lấy được data url: tại e.target.result
(vâng, e.target
là đối tượng reader
, nhưng tôi đã có vấn đề khi sử dụng reader
thay cho e.target
bên trong hàm này) . Chúng tôi sẽ chuyển data url này đến hàm showUploadedItem
của chúng tôi (chúng tôi đã viết ở trên).
Tiếp theo, chúng ta kiểm tra đối tượng formdata
. Hãy nhớ rằng, nếu trình duyệt hỗ trợ FormData
, formdata
sẽ là một đối tượng FormDat
a; nếu không, kết quả sẽ là false.
Vì vậy, nếu chúng ta có một đối tượng FormData,
chúng ta sẽ gọi phương thức append
. Mục đích của đối tượng FormData
là giữ các giá trị mà bạn đang gửi qua một form; do đó, phương thức append
chỉ cần có một key và một giá trị. Trong trường hợp của chúng tôi, trọng tâm của chúng tôi là ảng images[]
; bằng cách thêm dấu ngoặc vuông vào cuối, chúng tôi đảm bảo mỗi lần chúng tôi append
(thêm vào) một giá trị khác, chúng tôi đang thực sự bổ sung vào mảng đó, thay vì ghi đè lên thuộc tính image.
Chúng ta đã gần xong. Trong vòng lặp for, chúng ta đã hiển thị từng hình ảnh cho người dùng và thêm chúng vào đối tượng formdata
. Bây giờ, chúng ta chỉ cần tải lên các hình ảnh. Bên ngoài vòng lặp for
, đây là phần cuối cùng của câu đố của chúng tôi:
1 |
if (formdata) { |
2 |
$.ajax({ |
3 |
url: "upload.php", |
4 |
type: "POST", |
5 |
data: formdata, |
6 |
processData: false, |
7 |
contentType: false, |
8 |
success: function (res) { |
9 |
document.getElementById("response").innerHTML = res; |
10 |
} |
11 |
}); |
12 |
} |
Lần nữa, chúng ta phải chắc rằng chúng ta có FormData
hỗ trợ; nếu không có, nút "Upload Files!" sẽ được hiển thị. và đó là cách người dùng sẽ upload các ảnh chụp. Tuy nhiên, nếu chúng ta có FormData
hỗ trợ, chúng ta sẽ chuyển qua việc upload qua AJAX. Chúng ta đang sử dụng jQuery để xử lý tất cả sự vụ của AJAX qua các trình duyệt.
Có thể bạn quen thuộc với jQuery và phương thức $.ajax:
bạn truyền vào một đối tượng chọn lựa: url
, type
và thuộc tính success
là bắt buộc. Thuộc tính data
là đối tượng formdata
của chúng ta. Chú ý những thuộc tính processData
và contentType.
Dựa theo tài liệu của jQuery, processData
là true
mặc định, và sẽ xử lý và chuyển đổi dữ liệu thành một chuỗi query. Chúng ta không muốn làm điều đó, vì thế chúng ta xét nó về false
. Chúng ta cũng thiết lập contentType
sang false
để chắc rằng data đưa về máy chủ giống như chúng ta mong đợi.
Và thế đấy. Bây giờ, khi người dùng tải trang, họ sẽ thấy:



và sau khi họ chọn hình ảnh, họ sẽ nhìn thấy điều này:



Và hình ảnh đã được đăng tải lên:

Đấy là phần tổng kết!
Tải hình ảnh lên qua AJAX rất tuyệt, và điều hay ho là những kỹ thuật mới này hỗ trợ nó mà không cần phải có thêm những mánh khoé dông dài. Nếu bạn có thắc mắc về những điều chúng ta vừa thực hiện, hãy để lại bình luận. Rất cảm ơn đã đọc bài viết!
Và nếu bạn vẫn cần sự giúp đỡ về các vấn đề coding, hãy tìm sự hỗ trợ từ Envato Studio.