Split file & merge file upload cùng với PHP

Hôm nay nhận được yêu cầu phải làm chức năng này qua đó tiện thể mình sẽ viết một bài viết chia sẻ với các bạn. Để có thể hoàn thành được chức năng split & merge file này thì các bạn cần lắm được cơ bản các kiến thức về hàm xử lý file trong PHP e.g hàm mở file fopen(), hàm đọc file ...

Hôm nay nhận được yêu cầu phải làm chức năng này qua đó tiện thể mình sẽ viết một bài viết chia sẻ với các bạn. 

Để có thể hoàn thành được chức năng split & merge file này thì các bạn cần lắm được cơ bản các kiến thức về hàm xử lý file trong PHP e.g hàm mở file fopen(), hàm đọc file fgets() và hàm ghi file fwrite(). Hoặc nếu bạn chưa lắm được kiến thức này thì các bạn có thể xem lại bài viết các hàm xử lý file trong php.

Trong bài viết này mình sẽ không nói lại cách upload một file từ client nên server-side nữa. Nếu bạn chưa lắm được phương pháp để upload một file từ client nên server-side thì bạn có thể xem lại các bài viết của mình.

  • Upload file trong PHP - Xem
  • Upload ảnh bằng ajax và PHP - Xem
  • Upload file sử dụng Uploadifive - Xem

Trong ba vài viết này mình đã trình bày rất kỹ rồi. Trong bài viết này mình sẽ không nói lại nữa.

Trong bài viết này mình sẽ tập chung vào nội dung chính là split & merge file. Mình sẽ đưa da một giả thiết là mình có một file như sau:

$filepath = './uploads/example.doc'; // filesize = 500KB 

Bây giờ mình muốn chia nó thành khoảng 5 phần nhỏ hơn thì mỗi phần sẽ có dung lượng là 500KB / 5 = 100KB.

$partnum = 5; // số phần sẽ chia
$filezise = filesize($filepath) // Size file đích.
$partsize = floor($filezise/partnum)// Size mỗi phần sẽ chia nhỏ.
//Return $partsize = 100KB

Hoặc khi bạn cần download file này thì 5 phần kia sẽ được gộp lại thành một file ban đầu.

P/s: Trong nội dung bài viết này mình sẽ không tạo ra CSDL để lưu chữ bất cứ thông tin gì. Nhưng nếu bạn có ý đinh viết chương trình quản lý upload và download file có sử dụng phương pháp split & merge file thì bạn nên sử dụng CSDL. Lưu ý rằng mình chỉ nói về ý tưởng thôi nhé.

Mình sẽ tạo ra một class có tên là SplitMergeFile. Mình sẽ xây dựng một số hàm sau.

  • __constructor($dir, filepath, $partnum):Hàm khởi tạo
  • _basename(): Hàm này sẽ trả về tên đây đủ của file
  • rmkdir(): Hàm tạo danh mục đệ quy
  • _mkdir(): Hàm tạo một danh mục
  • split_file(): Hàm cắt file
  • merge_file(): Hàm merge file

Chi tiết nội dung SplitMergeFile:

class SplitMergeFile{
	/**/
	private $dir = null;
	private $filepath = null;
	private $partnum = 1;
	/*
	|--------------------------------------------------------------------------
	| Constructor
	|--------------------------------------------------------------------------
	*/
	function __construct($dir, $filepath, $partnum){
		$this->dir = $dir;
		$this->filepath = $filepath;
		$this->partnum = $partnum;
	}
	/*
	|--------------------------------------------------------------------------
	| Return basename of file
	|--------------------------------------------------------------------------
	*/
	function _basename(){
		$path_parts = @pathinfo($this->filepath);
		return $path_parts['basename'];
	}
	/*
	|--------------------------------------------------------------------------
	| Creates directories recursively
	|--------------------------------------------------------------------------
	*/
	function rmkdir($path, $mode = 0777) {
		return is_dir($path) || ( $this->rmkdir(dirname($path), $mode) && $this->_mkdir($path, $mode) );
	}
	/*
	|--------------------------------------------------------------------------
	| Creates directory
	|--------------------------------------------------------------------------
	*/
	function _mkdir($path, $mode = 0777) {
		$old = umask(0);
		$res = @mkdir($path, $mode);
		umask($old);
		return $res;
	}
	/*
	|--------------------------------------------------------------------------
	| Function Split File
	|--------------------------------------------------------------------------
	*/
	function split_file(){
		$handle = @fopen($this->filepath, 'rb') or die("Lỗi mở file {$this->filepath}");
		// Tổng dung lượng File đích
		$filesize = @filesize($this->filepath);
		// Tính dung lượng mỗi phần
		$partsize = floor($filesize/$this->partnum);
		$excess = $filesize % $this->partnum; // Phần dư thừa
		$parts = array();
		for($i=0; $i<$this->partnum; $i++){
			if($i==$this->partnum-1 && $excess > 0){
				$parts[$i] = fread($handle, $partsize + $excess); 
			}else{
				$parts[$i] = fread($handle, $partsize); 
			}
		}
		// Đóng mở file đích
		@fclose($handle) or die("Lỗi đóng File");
		
		// Ghi dữ liệu vào từng file đã được chia
		$dirname = $this->dir.'/'.$this->_basename();
		if(!is_dir($dirname)) $this->rmkdir($dirname, 0777);
		for($i=0; $i<$this->partnum;$i++) { 
			$handle = fopen($dirname.'/splited_'.$i, 'wb') or die("Lỗi mở File"); 
			@fwrite($handle,$parts[$i]) or die("Lỗi ghi File Splitter"); 
		} 
		// Close file handle 
		@fclose($handle) or die("Lỗi đóng File Splitter"); 
	}
	/*
	|--------------------------------------------------------------------------
	| Function Merge File
	|--------------------------------------------------------------------------
	*/
	function merge_file() { 
		$content='; 
		// Đọc từng file đã chia và merge 
		for($i=0; $i<$this->partnum; $i++) { 
			$file_size = @filesize($this->dir.'/'.$this->_basename().'/splited_'.$i); 
			$handle = @fopen($this->dir.'/'.$this->_basename().'/splited_'.$i, 'rb') or die("Lỗi mở File"); 
			$content .= @fread($handle, $file_size) or die("Lỗi đọc File"); 
		} 
		// Ghi tới merge File
		$handle=fopen($this->dir.'/merge/'.$this->_basename(), 'wb') or die("Lỗi không thể tạo Merge File"); 
		@fwrite($handle, $content) or die("Lỗi ghi merge File"); 
		// Đóng mở File ghi
		@fclose($handle) or die("Lỗi đóng File"); 
	}
}

P/s: Có lẽ các bạn đọc qua cũng sẽ hiểu tuy nhiên mình lưu ý các bạn phần này.

$excess = $filesize % $this->partnum; // Phần dư thừa
for($i=0; $i<$this->partnum; $i++){
	if($i==$this->partnum-1 && $excess > 0){
		$parts[$i] = fread($handle, $partsize + $excess); 
	}else{
		$parts[$i] = fread($handle, $partsize); 
	}
}

Tại sao lại vậy? Mình sẽ giải thích cho các bạn hiểu nhé.

// Giả sử bạn có một file dung lượng
$filesize = 508KB; 

// Mình sẽ chia nhỏ làm 5 phần
$partnum = 5;

// Tính dung lượng mỗi phần
$partsize = floor($filesize/$partnum); 

// Như các bạn đã biết thì hàm floor() làm tròn giá trị xuống mức sàn.
// Khi đó kết quả của mối phần: $partsize = 101;

// Nếu lấy $partsize(101) * $partnum(5) = 505(KB)
// Sau khi tính dung lượng bị thiếu so với ban đầu $filesize = 508KB
// 508/5 ~= 101.6 => Mỗi phần đã bị hàm floor() làm thiếu đi một ít.

// Xác định phần thiếu
$excess = $filesize % $this->partnum;

// Phần thiếu này sẽ được gán vào phần cuối cùng.
if($i==$this->partnum-1 && $excess > 0){
	$parts[$i] = fread($handle, $partsize + $excess); 
}

Bây giờ thì các bạn đã hiểu rồi đúng không nào???

Ở đây mỗi khi chia nhỏ một file mình sẽ tạo mới một folder trùng với tên của file.

$filepath = './uploads/example.doc';
$folder= example.doc

Thực ra mình làm vậy để mình khôi phục lại file ban đầu cho đỡ dài dòng thôi. Còn khi vào thực tế thì có nhiều cách khác.

Bây giờ chúng ta chạy thử và xem thành quả nhé.

$dir = './uploads/splitmerge/';
$filepath = './uploads/content/cau-lenh-sql.jpg';
$smf = new SplitMergeFile($dir, $filepath, 5);

// Split file
$smf ->split_file();

// Merge file
$smf->merge_file();

Kết quả:

Kết luận.

Như vậy là mình đã giới thiệu tới các bạn cách chia nhỏ một file lớn và gộp lại các file nhỏ đã chia thành file ban đầu. Tuy bài viết còn khá lộn xộn như mình đã nói ban đầu đây chỉ là giải pháp và mình đã ứng dụng nó vào sản phẩm khách hàng. Có lẽ nó sẽ phải tối ưu hơn nữa nhưng ở thời điểm bây giờ thời gian nó chưa cho phép nên đành để sau vậy.

Lưu ý: Các thư mục bạn cần thiết lập permission là 0777

Bài liên quan

Kiểm tra File upload trước khi submit với HTML5 và Javascript

Việc kiểm tra file trước khi upload luôn luôn là việc làm cần thiết cho bất kì website hay blog nào. Trước đây, để kiểm tra file có dung lượng quá lớn, hay file đó không được hỗ trợ để upload… Chúng ta thường phải gửi file đó lên server , rồi mới dùng ngôn ngữ lập trình như PHP để kiểm tra ...

Hoàng Hải Đăng viết 20:12 ngày 04/10/2018

Tạo File Upload hàng loạt với Ajax và PHP

Hôm nay mình sẽ chia sẻ cho các bạn một bài viết về PHP và cách sử dụng Ajax với ứng dụng upload file. Với ứng dụng này các bạn có thể áp dụng vào trong các trang web của mình, ngoài ra các bạn có thể học và hiểu cách sử dụng Ajax, bài viết này rất hữu ích cho những bạn mới bước vào nghề web. ...

Vũ Văn Thanh viết 17:58 ngày 04/10/2018

Tạo chức năng upload file với PHP và MySQL

Hello các bạn ! Lúc trước mình có chia sẻ cho các bạn một bài viết Tạo trang upload hình ảnh bằng PHP , nhưng xem ra có rất nhiều bạn gặp vấn đề khi thực thi bài viết này. Hôm nay mình sẽ giới thiệu cho các bạn một cách làm khác, với đầy đủ chức năng hơn, đặc biệt là có thể kết nối cơ sở dữ liệu ...

Vũ Văn Thanh viết 17:57 ngày 04/10/2018

Split file & merge file upload cùng với PHP

Hôm nay nhận được yêu cầu phải làm chức năng này qua đó tiện thể mình sẽ viết một bài viết chia sẻ với các bạn. Để có thể hoàn thành được chức năng split & merge file này thì các bạn cần lắm được cơ bản các kiến thức về hàm xử lý file trong PHP e.g hàm mở file fopen(), hàm đọc file ...

Bùi Văn Nam viết 16:33 ngày 01/10/2018

Hướng dẫn upload file lên server với php

Tổng hợp các hàm xử lý mảng trong php Tổng hợp các hàm xử lý file trong php Tổng hợp các hàm kiểm tra dữ liệu trong php Tìm hiểu session và cookie trong php Cách Upload file lên server đơn giản với php dành cho những bạn mới bắt đầu học lập trình được mình hướng dẫn chi tiết dưới đây ...

Hoàng Hải Đăng viết 17:18 ngày 04/09/2018
0