圖片上傳并回顯后端篇
我們先看一下效果
繼上一篇的圖片上傳和回顯,我們來(lái)實(shí)戰(zhàn)一下圖片上傳的整個(gè)過(guò)程榔至,今天我們將打通前后端种冬,我們來(lái)真實(shí)的了解一下,我們上傳的文件宣谈,是以什么樣的形式上傳到服務(wù)器,難道也是一張圖片?等下我們來(lái)揭曉
我們?cè)趯?shí)戰(zhàn)開(kāi)始前呢键科,我們先做一下準(zhǔn)備工作闻丑,比如新建一個(gè)java web工程,如果你不懂這個(gè)的話勋颖,那我建議你先學(xué)一下Javaweb嗦嗡,可以去我的公眾號(hào)找一下這方面的教程。我們就給我們的工程起名為UpImg饭玲,我們?cè)俳o他建一個(gè)web包和util包侥祭,再把我們以前前端做的圖片回顯的代碼拷到工程里,我們來(lái)看一下項(xiàng)目
我們發(fā)布一下項(xiàng)目來(lái)看一下
這樣的話,我們基本的框架就做好了卑硫,我們今天就先用form表單來(lái)實(shí)戰(zhàn)一下圖片的上傳徒恋,下一期我們就通過(guò)ajax來(lái)實(shí)現(xiàn)異步圖片上傳,我們先給我們的前端代碼加點(diǎn)料
<form action="upload" method="post" enctype="multipart/form-data">
<div class="uploadImgBtn" id="uploadImgBtn">
<input class="uploadImg" type="file" name="file" multiple id="file">
</div>
<input type="submit" value="上傳">
</form>
這個(gè)樣式我就不再美化了欢伏,我們來(lái)看一下效果
這樣的話入挣,我們前端基本就完成了,我來(lái)講解一下部分代碼吧硝拧;表單的enctype屬性:
1径筏、默認(rèn)屬性:application/x-www-form-urlencoded,只處理表單域中的value屬性值障陶,采用這種編碼的方式的表單會(huì)將表單域的值處理成url編碼方式
2滋恬、multipart/form-data,這種編碼方式的表單會(huì)以二進(jìn)制流的方法來(lái)處理表單數(shù)據(jù)抱究。這種編碼方式會(huì)將文件域指定文件的內(nèi)容也封裝到請(qǐng)求參數(shù)里
3恢氯、text/plain,這種方式主要適用于直接通過(guò)表單發(fā)送郵件的方式
接下來(lái)我們講解一下文件上傳的思路鼓寺,
1勋拟、先是表單提交
2妈候、對(duì)數(shù)據(jù)和附件進(jìn)行二進(jìn)制編碼
3、servlet中使用二進(jìn)制流獲取內(nèi)容
思路我們已經(jīng)知道了苦银,那我們就開(kāi)始編碼吧
我們先在util包下新建一個(gè)類,我就起名為UpImgUtils幔虏,接下來(lái)我們就編碼吧
package util;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import javax.servlet.http.HttpServletRequest;
/**
* upload Img Utils
*
* @author admin
*
*/
public class UpImgUtils {
/*
* 思路 1纺念、從request當(dāng)中獲取流信息
* 2所计、新建一個(gè)臨時(shí)文件,用輸出流指向這個(gè)文件
* 3、關(guān)閉流
*/
public static void keepFile(HttpServletRequest request) throws IOException {
// 1主胧、從request當(dāng)中獲取流信息
InputStream fileSource = request.getInputStream();
/*
* 臨時(shí)文件的存儲(chǔ)路徑(我們?cè)趙ebContent下新建一個(gè)temp文件夾,發(fā)布項(xiàng)目的時(shí)候很可能因?yàn)閠emp為空习勤,
* 沒(méi)在tomcat中建立一個(gè)文件夾,到時(shí)候自己在發(fā)布的項(xiàng)目中添加一個(gè)即可)
*/
String tempFileName = request.getServletContext().getRealPath("/") + "temp/tempfile.txt";
// 2图毕、新建一個(gè)臨時(shí)文件,用輸出流指向這個(gè)文件
// 建一個(gè)文件
File tempFile = new File( tempFileName );
// 用輸出流指向這個(gè)文件
FileOutputStream outputStream = new FileOutputStream( tempFile );
//我們就每次讀寫10K,我們的文件小予颤,這個(gè)就已經(jīng)夠用了
byte[] b = new byte[1024*10];
int n = 0 ;
//讀寫文件,-1標(biāo)識(shí)為空
while( (n = fileSource.read(b) ) != -1 ) {
outputStream.write(b, 0, n);
}
// 3冬阳、關(guān)閉流
fileSource.close();
outputStream.close();
}
}
這個(gè)類就是用來(lái)讀取form表單傳來(lái)的字節(jié)流党饮,寫到一個(gè)臨時(shí)文件中,我們就一個(gè)servlet來(lái)調(diào)用一下我們的工具來(lái)看看效果氯窍。
package web;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import util.UpImgUtils;
public class upload extends HttpServlet {
private static final long serialVersionUID = 1L;
public upload() {
super();
// TODO Auto-generated constructor stub
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// TODO Auto-generated method stub
UpImgUtils.keepFile(request);
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// TODO Auto-generated method stub
doGet(request, response);
}
}
代碼已經(jīng)寫好狼讨,我的項(xiàng)目是java web項(xiàng)目2.5的版本柒竞,會(huì)自動(dòng)配置servlet,配置的話布隔,就不再講解踩晶。我們來(lái)運(yùn)行看一下效果
我們已經(jīng)看到了渡蜻,實(shí)際上文件上傳就是把文件的二進(jìn)制流上傳到服務(wù)端术吝,這難道就結(jié)束了嗎?
那肯定不可能啊排苍,我們上傳的是個(gè)圖片学密,那我們肯定希望還是圖片啊,我們就來(lái)重新封裝一個(gè)工具類彤守,在封裝之前哭靖,我們先看一下臨時(shí)文件的格式
這是我隨便找的兩個(gè)文件,上傳后生成的臨時(shí)文件筝蚕,我們就不實(shí)戰(zhàn)封裝兩個(gè)文件了,我們就實(shí)戰(zhàn)一下封裝一個(gè)臨時(shí)文件,因此呢我們先把input標(biāo)簽中的multiple屬性去掉绿映,把我們的前端自動(dòng)生成input標(biāo)簽的代碼也先注釋掉屏箍,我們先看一下改動(dòng)的代碼
<script>
$(document).ready(function(){
//為外面的盒子綁定一個(gè)點(diǎn)擊事件
$("#uploadImgBtn").click(function(){
/*
1赴魁、先獲取input標(biāo)簽
2、給input標(biāo)簽綁定change事件
3颖御、把圖片回顯
*/
// 1潘拱、先回去input標(biāo)簽
var $input = $("#file");
console.log($input)
// 2、給input標(biāo)簽綁定change事件
$input.on("change" , function(){
console.log(this)
//補(bǔ)充說(shuō)明:因?yàn)槲覀兘oinput標(biāo)簽設(shè)置multiple屬性瘪弓,因此一次可以上傳多個(gè)文件
//獲取選擇圖片的個(gè)數(shù)
var files = this.files;
var length = files.length;
console.log("選擇了"+length+"張圖片");
//3禽最、回顯
$.each(files,function(key,value){
//每次都只會(huì)遍歷一個(gè)圖片數(shù)據(jù)
var div = document.createElement("div"),
img = document.createElement("img");
div.className = "pic";
var fr = new FileReader();
fr.onload = function(){
img.src=this.result;
div.appendChild(img);
document.body.appendChild(div);
}
fr.readAsDataURL(value);
})
})
//把這下面的注釋掉即可
// //4川无、我們把當(dāng)前input標(biāo)簽的id屬性remove
// $input.removeAttr("id");
// //我們做個(gè)標(biāo)記,再class中再添加一個(gè)類名就叫test
// var newInput = '<input class="uploadImg test" type="file" name="file" multiple id="file">';
// $(this).append($(newInput));
})
})
</script>
我們來(lái)看一下一個(gè)文件的時(shí)候晾虑,臨時(shí)文件的格式
我們來(lái)分析一下仅叫,第二行的filename是我們需要的诫咱,這是文件的名稱,我們已經(jīng)看到中文名稱亂碼,一會(huì)編碼的時(shí)候,我們需要解決一下凯亮;第4行有一個(gè)空行哄尔,到第5行的時(shí)候才到我們的正文部分;我們的正文結(jié)束的時(shí)候會(huì)有一個(gè)空格富拗;既然知道了這些鸣戴,我們就去完善一下我們的工具類吧
package util;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;
import javax.servlet.http.HttpServletRequest;
/**
* upload Img Utils
*
* @author admin
*
*/
public class UpImgUtils {
/*
* 思路 1窄锅、從request當(dāng)中獲取流信息
* 2、新建一個(gè)臨時(shí)文件,用輸出流指向這個(gè)文件
* 3追驴、關(guān)閉流
*/
public static void keepFile(HttpServletRequest request) throws IOException {
// 1疏之、從request當(dāng)中獲取流信息
InputStream fileSource = request.getInputStream();
/*
* 臨時(shí)文件的存儲(chǔ)路徑(我們?cè)趙ebContent下新建一個(gè)temp文件夾,發(fā)布項(xiàng)目的時(shí)候很可能因?yàn)閠emp為空丙曙,
* 沒(méi)在tomcat中建立一個(gè)文件夾几缭,到時(shí)候自己在發(fā)布的項(xiàng)目中添加一個(gè)即可)
*/
String tempFileName = request.getServletContext().getRealPath("/") + "temp/tempfile.txt";
//2年栓、新建一個(gè)臨時(shí)文件,用輸出流指向這個(gè)文件
//建一個(gè)文件
File tempFile = new File( tempFileName );
//用輸出流指向這個(gè)文件
FileOutputStream outputStream = new FileOutputStream( tempFile );
//我們就每次讀寫10K,我們的文件小,這個(gè)就已經(jīng)夠用了
byte[] b = new byte[1024*10];
int n = 0 ;
//讀寫文件,-1標(biāo)識(shí)為空
while( (n = fileSource.read(b) ) != -1 ) {
outputStream.write(b, 0, n);
}
//3纸兔、關(guān)閉流
fileSource.close();
outputStream.close();
//第二部分......................................................
/**
* 思路
* 1否副、獲取文件的名稱,并解決中文亂碼
* 2洲拇、獲取文件的內(nèi)容
* 3、保存文件
*/
//第二部分 1男翰、獲取文件的名稱纽乱,并解決中文亂碼
RandomAccessFile randomFile = new RandomAccessFile(tempFile,"r");
randomFile.readLine();//先讀取一行
String str = randomFile.readLine();//讀取第二行
int beginIndex = str.lastIndexOf("filename=\"") + 10;//定位到文件名開(kāi)始的地方
int endIndex = str.lastIndexOf("\"");//定位到文件名結(jié)尾的地方
String filename = str.substring(beginIndex, endIndex);
//判斷文件名是全路徑名還是只是文件名(google和火狐是只是文件名鸦列,微軟系列是全路徑名)
endIndex = filename.lastIndexOf("\\") + 1;
if( endIndex > -1 ) {
filename = filename.substring(endIndex);
}
//經(jīng)過(guò)上面的這幾步,我們就已經(jīng)獲取到了文件名顽爹,我們還需要解決一下中文名亂碼的問(wèn)題
//解決上傳文件中文名字亂碼
filename = new String(filename.getBytes("ISO-8859-1"), "UTF-8");
System.out.println("filename: " + filename );
//第二部分 2应民、獲取文件的內(nèi)容
//重新定位文件指針到文件頭
randomFile.seek(0);
long startPosition = 0L;//正文開(kāi)始的位置
int i = 1;
while( ( n = randomFile.readByte() ) != -1 && i <=4 ) {
if( n == '\n') {
startPosition = randomFile.getFilePointer();
i++;
}
}
//
startPosition = randomFile.getFilePointer() - 1 ;
//獲取文件內(nèi)容诲锹,結(jié)束位置
randomFile.seek(randomFile.length() );//指針定位到尾部
long endPosition = randomFile.getFilePointer();
int j = 1;
while( endPosition >= 0 && j <=2 ) {
endPosition--;
randomFile.seek(endPosition);
if(randomFile.readByte() == '\n' ) {
j++;
}
}
endPosition = endPosition - 1;
//第二部分 3、保存文件
//設(shè)置保存上傳文件的路徑黄虱,我們好保存到temp中
String realPath = request.getServletContext().getRealPath("/") + "temp";
File fileupload = new File( realPath );
File saveFile = new File(realPath,filename);
RandomAccessFile randomAccessFile = new RandomAccessFile(saveFile,"rw");
//
//從臨時(shí)文件當(dāng)中讀取文件內(nèi)容(根據(jù)起止位置獲扔褂铡)
randomFile.seek(startPosition);
while(startPosition < endPosition ) {
randomAccessFile.write(randomFile.readByte());
startPosition = randomFile.getFilePointer();
}
//
//關(guān)閉輸入輸出流桥爽、刪除臨時(shí)文件
randomAccessFile.close();
randomFile.close();
//tempFile.delete();
}
}
我們來(lái)看一下效果
這樣的話,我們的上傳圖片也已經(jīng)上傳成功了盗扒,我們來(lái)把上傳圖片的url反回給前端吧缀去,這些代碼就不再展示,自己實(shí)現(xiàn)一下吧褥影。