HTML多圖上傳中的問題探索

在編寫實現“天雲圖牀”這個程序的時候,想要實現批量上傳圖片的操作,便於自己和廣大站長的使用,其中遇到了一些簡單但又不常見的問題,網上資料良莠不齊,遂總結一遍

一開始,天雲圖牀只有單圖上傳的功能,開源到技術QQ羣中,大家都建議小東寫一個多圖上傳的功能,於是趁着週六有時間,開始編寫代碼。

本以爲是一個非常簡單的工作,哪知道弄了一整天的時間。

0x01一開始的思路

一開始,後臺API服務僅支持單圖上傳,想着,不動服務器的代碼,嘗試從客戶端來實現。

開發前的環境:

新浪圖牀服務端僅支持單圖(form表單形式)上傳圖片即可

前端(客戶端 https://api.top15.cn/picbed/)使用 ajax,實例 form表單 內容,發送給服務端,服務端接受數據,讀取$_FILES中的文件

服務端(PHP),讀取post過來的文件,然後模擬POST遠程上傳新浪服務器,新浪圖牀的接受參數爲`base64`編碼過的圖片資源。

0x02 客戶端的問題

在這之前,參考了一些開源項目,比如 ssi-uploaderzuypload 這些基於 jquery 的三方插件,他們的共同功能是,能夠讀物本地文件,並將本地圖片展示在前端頁面上。

站長之家中多圖上傳案例

在站長之家的網站上找到如上的多圖上傳案例,支持拖拽和多圖上傳,想着自己是否可以嘗試寫一下吶?

首先客戶端中 HTML 代碼需要注意 inputname 屬性值要爲數組,否則上傳了數據,在服務端值會接受一個文件數據

如下是我在實現過程中的方式,並設置了隱藏,因爲本身 input 文件組件不夠美觀,用另外的按鈕來控制此組件。

<form id="form" enctype="multipart/form-data">
	<input type="file" name="files[]" multiple="multiple" id="files" accept="image/png,image/gif,image/jpg,image/jpeg" style="display: none;">
</form>

與此同時,想要實現讀取 files 中的圖片並顯示在界面上,那麼就需要監聽 input[type=file] 組件的變換

<script src="https://libs.baidu.com/jquery/2.0.0/jquery.min.js"></script>
<script type="javascript/text">
	$(function(){
		$("#files").change(function(){
			console.log($("#files")[0].files)	//得到一個FileList對象
		})
	})
</script>

上述代碼在控制檯打印上傳文件的信息,可以看到如下情況:

files內容

files中,包含了文件的大小,名字,時間和相對路徑


0x03 如何展示預覽圖片?

HTML5 中有一個 FileReader() 的東西,能夠讀取文件內容,

// javascript

function loadImg(){
    //獲取文件
    var file = $("#files")[0].files[0];

    //創建讀取文件的對象
    var reader = new FileReader();

    //創建文件讀取相關的變量
    var imgFile;

    //爲文件讀取成功設置事件
    reader.onload=function(e) {
        imgFileUrl = e.target.result;
         $("#gallery").append('<img src="'+imgFileUrl+'" width="200px" height="200px">');
    };
    reader.readAsDataURL(file);
}


//////////////////////////////////////////////////////////////
FileReader對象的方法
方法名	              參數	          描述
readAsBinaryString	file	          將文件讀取爲二進制編碼
readAsText	        file,[encoding]	  將文件讀取爲文本
readAsDataURL	    file	          將文件讀取爲DataURL
abort	            (none)	          終端讀取操作

在控制檯中執行上述代碼,然後運行 loadImg() 函數,即可打印準備上傳文件中的第一張圖片內容到控制檯,類似這樣的字符串 data:image/png;base64,iVBORw0KGgoAAAANSUhEU...,這是將圖片進行了base64編碼,服務端只需要解碼base64即可恢復原文件。將內容複製瀏覽器的地址欄打開是沒法兒預覽的,小東現在想了一下,其原因主要是:瀏覽器地址欄,類似GET請求,限制了URL的長度和大小,故無法正常顯示圖片(此處浪費了較多時間,一直以爲讀取出來的數據不對!!!其實是完全正確的)。

然後本地寫了一個測試文件,如下:

base64

不同文件的數據頭部是不一樣的:data:image/png;base64,這是上傳了一個 png 圖片的案例,上傳 jpeg,gif等等頭部都會改變,真正有效的是,(逗號)後面的內容,纔是文件內容的編碼,客戶端直接提交整個參數即可無需處理,處理交給服務端即可。

服務端支持一個圖片上傳,那麼我們循環讀取,然後上傳即可,至於返回的數據,以json方式傳輸,將單個上傳信息直接array_push()到一個數組即可。

服務端代碼的改寫(部分):

<?php

$result = [];
foreach ($_POST as $key => $value) {
	$file = $value;			//BASE64
	$cookie = $config['cookie'];
	$file = explode(',', $file);
	$imgarr = upload($file[1], $type, $cookie);		//TYPE是GET得到,值爲bs64,避免post內容混淆
	array_push($result, $imgarr);
}
echo json_encode($result);
exit();

?>

至此,客戶端 base64 版本及服務端改造完成。


0x04 FormData版本

Javascript中,有Jquery中的 FormData方法,使得 Form表單以 ajax 方法傳輸變得可行。

博主個人覺得這種方式更好,代碼具有簡潔性,同時後端按照傳統方式來處理

前端代碼(javascript):

<script type="text/javascript">
		$(function(){

			//選擇圖片
			$('.btn-primary').click(function(){
				$('#files').trigger('click');
			})

			$('#files').change(function(){
				var fileNum = $("#files")[0].files.length;
				$('.btn-primary').text('上傳文件('+fileNum+')');
			})

			//點擊上傳圖片
			$('#upload_btn').click(function(){
              	$("#upload_btn").text("上傳中...");
				$('#upload_btn').attr('disabled', true);
				var fileNum = $("#files")[0].files.length;
				if(fileNum && fileNum <= 10){
					$.ajax({
				        url : "https://api.top15.cn/picbed/picApi.php?type=multipart&num=multiple",		//此API可供大家免費使用,且行且珍惜
				        type : 'POST',
				        cache: false,
				        data : new FormData($('#form')[0]),
				        processData : false,
				        contentType : false,
				        async : false,
				        success : function(data) {
				            if(data){
				            	var str = '';
				            	var str2 = '';
				            	$.each(data, function(index, obj){
				            		str += '<a href="'+ obj.url2+'" target="_blank">'+obj.url2+'</a><br>';
				            		str2 += '<img src="'+obj.url2+'" width="'+obj.width+'" height="'+obj.height+'">';
				            	})
				            	$(".imgs_src").html(str);
				            	$(".textarea").text(str2);
				            }
				        }
				    });
				    $("#upload_btn").attr('disabled', false);
				    $('#files').val('');
					$('.btn-primary').text('上傳文件(0)');

				}
				else{
					alert("請選擇上傳文件,且上傳文件數不超過10!");
					$('#files').val('');
					$('.btn-primary').text('上傳文件(0)');
					$("#upload_btn").attr('disabled', false);
				}
              $("#upload_btn").text("一鍵上傳");
			})
		})
</script>

後端處理:

<?php
	$result = [];
    foreach ($_FILES['files']['tmp_name'] as $key => $value) {		//從$_FILES中讀取文件
      $file = $value;
      $cookie = $config['cookie'];
      $imgarr = upload($file, $type, $cookie);
      array_push($result, $imgarr);
    }
    echo json_encode($result);
    exit();
?>

0x05 最終完成:

完成項目的展示網站:https://api.top15.cn/picbed/

開源地址:https://github.com/dyboy2017/sinaPictureBed

天雲圖牀

0x06 總結

本次程序實現中,input 控件中的 name 屬性值要爲數組,其次 HTML5 功能 FileReader() 是一個不錯的方法,有利於及時展示即將上傳的圖片,一些多圖上傳的插件,使用的就是此種方式,但是會耗費客戶端較多的計算資源,可能導致客戶端的卡頓(可以限制上傳圖片的大小以及數量是一種解決方案),簡單的直接使用 Jquery 中的 FormData() 即可使用 ajax異步傳輸數據,最後測試,發現在 Android 手機瀏覽器上還是無法多圖上傳,但是在 Iphone 手機上可以多圖上傳,在PC端,360瀏覽器運行該頁面會卡頓但不影響正常操作,在 Firfox 瀏覽器上表現正常!HTML5特性在需要的查詢文檔使用即可。

原文地址:https://blog.dyboy.cn/program/110.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章