最近項目上有遇到需要在手機APP端嵌入HTML5頁面并選取照片上傳绘盟,網(wǎng)上有很多方式實現(xiàn)麻惶,原本使用了百度的webupload插件溶推,但是需要依賴flash碍脏,所以我使用了H5自帶的FileReader API來讀取文件流轉(zhuǎn)換成base64字符串,然后使用ajax無刷新方式上傳到服務(wù)器卖擅。
HTML5頁面編寫
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=Edge,chrome=1">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0"/>
<script type="text/javascript" src="assets/aries/lib/jquery.js"></script>
<script type="text/javascript" src="js/json2/json2.js"></script>
<style>
.divImg{ border:1px solid #000; width:auto; height:auto;max-width: 100%;max-height: 100%}
.divImg img{width:100px; height:100px}
</style>
<script type="text/javascript">
var imgFiles = null;//用于存放base64字符串的數(shù)組
$(function(){
var input = $("#imgFile");
var result,div;
if(typeof FileReader==='undefined'){
input.setAttribute('disabled','disabled');
return alert("抱歉鸣奔,你的瀏覽器不支持 FileReader");
}else{
$("#imgFile").bind('change',readFile);
}
function readFile(){
imgFiles = new Array();
for(var i=0;i<this.files.length;i++){
var imgName = $("#imgFile").val().toLocaleLowerCase();
if (!imgName.match(/.jpg|.jpeg|.gif|.png|.bmp/i)){ //判斷上傳文件格式
return alert("上傳的圖片格式不正確,請重新選擇");
}
var reader = new FileReader();
reader.readAsDataURL(this.files[i]);
var picId = 0;
reader.onload = function(e){
imgFiles.push(this.result);//this.result就是圖片轉(zhuǎn)換后的base64字符串
result = '<div id="preview'+picId+'" class="divImg">![]('+this.result+')</div>';
div = document.createElement('div');
div.innerHTML = result;
document.getElementById('body').appendChild(div);//插入dom樹
picId++;
}
}
}
$('#btnUpload').bind('click',uploadImg);
$('#btnReset').bind('click',reset);
})
/**
* 上傳文件
*/
function uploadImg(){
for(var i=0;i<imgFiles.length;i++){
var base64 = imgFiles[i];
$.ajax({
url : 'http://192.168.1.102:8083/uploadFile',
type : 'post',
contentType: 'application/x-www-form-urlencoded; charset=utf-8',
async: false,
cache: false,
data : base64,
success : function(data){
console.log(data)
var result = JSON.parse(data);
if(result.isSuccess){
alert('第'+(i+1)+'張圖片上傳成功');
$('#preview'+i).remove();
}else{
alert('第'+(i+1)+'張圖片上傳失敗:'+result.resultDesc);
}
}
})
}
reset();
}
/**
* 重置表單
*/
function reset(){
$('#imgFile').val('');
$('.divImg').each(function(index,domEle){
$(this).remove();
})
}
</script>
</head>
<body id="body">
<form id="imgForm" action="http://localhost:8083/uploadFile" method="post" enctype="multipart/form-data"></form>
<label>請選擇一個圖像文件:</label>
<input type="file" id="imgFile" name="imgFile" accept="image/*" multiple />
<button id="btnUpload">上傳圖片</button>
<button id="btnReset">重置</button>
</body>
</html>
服務(wù)端處理
后臺使用原生的Java Servlet來接收處理
從HttpServletRequest中讀取前端傳來的InputStream并轉(zhuǎn)換成字符串
/**
* 從HttpServletRequest中讀取前端傳來的InputStream并轉(zhuǎn)換成base64字符串
*
* @param request
* @return
* @throws Exception
*/
private String getBase64Str(HttpServletRequest request) throws Exception {
try {
String imgStr = getRequestPayload(request);
if (imgStr == null || "".equals(imgStr)) {
return null;
}
/**
* 由于前端轉(zhuǎn)過來的數(shù)據(jù)格式為data:image/jpeg;base64,圖像base64字符串,因此需要處理一下
*/
final String subStr = "base64,";
String base64Str = imgStr.substring(imgStr.indexOf(subStr) + subStr.length(), imgStr.length());
return base64Str;
} catch (Exception ex) {
throw ex;
}
}
/**
* 從Request中讀取二進制數(shù)據(jù)流,并轉(zhuǎn)換成字符串
* @param request
* @return
* @throws Exception
*/
private String getRequestPayload(HttpServletRequest request) throws Exception{
StringBuilder sb = new StringBuilder();
BufferedReader reader = null;
try {
reader = request.getReader();
char[] buff = new char[1024];
int len;
while ((len = reader.read(buff)) != -1) {
sb.append(buff, 0, len);
}
} catch (IOException e) {
throw e;
}finally {
if(reader != null){
reader.close();
}
}
return sb.toString();
}
將base64字符串轉(zhuǎn)換成圖片并寫入磁盤惩阶,最終完整的代碼如下
import net.sf.json.JSONObject;
import org.apache.commons.io.IOUtils;
import sun.misc.BASE64Decoder;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
/**
* Created by qianlong on 2017/1/12.
*/
public class FileUploadServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
service(req,resp);
}
public void service(HttpServletRequest req, HttpServletResponse res)
throws IOException, ServletException {
JSONObject result = new JSONObject();
res.setContentType("text/html;charset=UTF-8");
// Create path components to save the file
final String path = "/home/bluecoffee/upload";//保存路徑可以從配置文件中讀取
final PrintWriter writer = res.getWriter();
try {
String base64Str = this.getBase64Str(req);
if(base64Str == null || base64Str.equals("")){
result.put("isSuccess", false);
result.put("resultDesc", "請選擇需要上傳的圖片");
writer.print(result.toString());
return;
}
System.out.println("base64Str=" + base64Str);
//生成一個新的文件名
String newImgName = path + File.separator + System.currentTimeMillis() + ".jpg";
boolean isWrite = this.writeImage(base64Str, newImgName);
if (isWrite) {
result.put("isSuccess", true);
result.put("resultDesc", "圖片上傳成功");
writer.print(result.toString());
}else{
result.put("isSuccess", false);
result.put("resultDesc", "寫入磁盤文件失敗");
writer.print(result.toString());
}
} catch (FileNotFoundException ex) {
ex.printStackTrace();
result.put("isSuccess", false);
result.put("resultDesc", "圖片不存在:"+ex);
writer.print(result.toString());
} catch (Exception ex){
ex.printStackTrace();
result.put("isSuccess", false);
result.put("resultDesc", "圖片上傳失敗:"+ex);
writer.print(result.toString());
}
}
/**
* 從HttpServletRequest中讀取前端傳來的InputStream并轉(zhuǎn)換成真正的圖片base64字符串
*
* @param request
* @return
* @throws Exception
*/
private String getBase64Str(HttpServletRequest request) throws Exception {
try {
String imgStr = getRequestPayload(request);
if (imgStr == null || "".equals(imgStr)) {
return null;
}
/**
* 由于前端轉(zhuǎn)過來的數(shù)據(jù)格式為data:image/jpeg;base64,圖像base64字符串,因此需要處理一下
*/
final String subStr = "base64,";
String base64Str = imgStr.substring(imgStr.indexOf(subStr) + subStr.length(), imgStr.length());
return base64Str;
} catch (Exception ex) {
throw ex;
}
}
/**
* 從Request中讀取二進制數(shù)據(jù)流,并轉(zhuǎn)換成字符串
* @param request
* @return
* @throws Exception
*/
private String getRequestPayload(HttpServletRequest request) throws Exception{
StringBuilder sb = new StringBuilder();
BufferedReader reader = null;
try {
reader = request.getReader();
char[] buff = new char[1024];
int len;
while ((len = reader.read(buff)) != -1) {
sb.append(buff, 0, len);
}
} catch (IOException e) {
throw e;
}finally {
if(reader != null){
reader.close();
}
}
return sb.toString();
}
/**
* 將base64字符串轉(zhuǎn)換成圖片并按新文件名寫入磁盤,
* @param base64Str
* @param newImgName
* @return
* @throws Exception
*/
private boolean writeImage(String base64Str, String newImgName) throws Exception {
if (base64Str == null) // 圖像數(shù)據(jù)為空
return false;
BASE64Decoder decoder = new BASE64Decoder();
OutputStream out = null;
try {
// Base64解碼
byte[] bytes = decoder.decodeBuffer(base64Str);
for (int i = 0; i < bytes.length; ++i) {
if (bytes[i] < 0) {// 調(diào)整異常數(shù)據(jù)
bytes[i] += 256;
}
}
//將圖片寫入磁盤
out = new FileOutputStream(newImgName);
out.write(bytes);
return true;
} catch (Exception e) {
return false;
} finally {
if (out != null) {
out.flush();
out.close();
}
}
}
}
web.xml中配置
<servlet>
<servlet-name>FileUploadServlet</servlet-name>
<servlet-class>com.bluecoffee.web.servlets.FileUploadServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>FileUploadServlet</servlet-name>
<url-pattern>/pc/uploadFile</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>FileUploadServlet</servlet-name>
<url-pattern>/phone/uploadFile</url-pattern>
</servlet-mapping>
小結(jié)
該示例在iPhone的safari瀏覽器挎狸、小米手機的瀏覽器測試過,還需要在更多移動端測試琳猫。下一步準備加入在移動端壓縮base64數(shù)據(jù)伟叛,提高傳輸效率。