業務場景
1、用戶未設置頭像時,根據一張大圖來隨機獲取圖上的的一個區域來作爲用戶的默認頭像
解決思路
思路1:將大圖平均分割成小圖,然後保存起來,提供程序讀取對應的地址。
思路2:動態讀取一張網絡圖片,根據所需的位置裁剪寫入http的返回體中。
參考文檔
1、javax.imageio.ImageIO;
2、java.awt.image.BufferedImage;
程序代碼
1、圖片裁剪的工具類
package tool;
import dto.http.HttpByteArray;
import org.springframework.util.StringUtils;
import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletResponse;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.util.List;
/**
* Created by yuyu on 2018/3/28.
* 裁剪圖片相關函數
* 根據 java.awt.image BufferedImage.getSubimage(int x, int y, int w, int h)
*/
public class CropPictureBuilder {
/**
* 將圖片裁剪成等分,保存成文件
*
* 判斷傳入名稱的圖片長度是否符合裁剪
* 根據裁剪的大小對圖片進行裁剪,不合適的部分不裁剪
*
* 返回裁剪成功的圖片個數
* @param fileName 需要裁剪的文件
* @param width 裁剪後每張圖片的框都
* @param height 裁剪後每張圖片的高度
* @return
* @throws Exception
*/
public static Integer cutToFile(String fileName, Integer width, Integer height) throws Exception {
FileInputStream fileInput = null;
Integer back=0;
try {
//檢驗傳入的拆分大小合法
if (width==null||height==null||width<=0||height<=0){
throw new Exception("拆分圖片異常,傳入裁剪尺寸必須大於零!");
}
//基礎數據
String format = CropPictureBuilder.getFormatName(fileName);//圖片類型
Integer imageWidth;//圖片寬度
Integer imageHeight;//圖片高度
Integer imageCutX = 0;//圖片開始裁剪的x軸
Integer imageCutY = 0;//圖片開始裁剪的Y軸
BufferedImage subImage;
File subFile;
File imageFile = new File(fileName);
//判斷文件是否存在
if (!imageFile.exists()){
throw new Exception("拆分圖片異常,圖片文件不存在!");
}
fileInput = new FileInputStream(imageFile);
BufferedImage sourceImg = ImageIO.read(fileInput);
//獲取圖片信息
imageWidth = sourceImg.getWidth();
imageHeight = sourceImg.getHeight();
//判斷傳入的長度是否滿足裁剪
while (imageCutX + width <= imageWidth && imageCutY + height <= imageHeight) {
//裁剪圖片
subImage = sourceImg.getSubimage(imageCutX, imageCutY, width, height);
subFile = FileNameBuilder.getFileByName(fileName +"-"+ back +"-x-" + imageCutX +
"-y-" + imageCutY + "." + format);
//寫入文件
ImageIO.write(subImage, format, subFile);
back++;
//偏移下一個位置
imageCutX += width;
//判斷下一個要裁剪的,是否跳轉下一行
if (imageCutX + width > imageWidth && imageCutY + height <= imageHeight) {
imageCutX = 0;
imageCutY += height;
}
}
} catch (Exception e) {
throw e;
} finally {
if (fileInput != null) {
fileInput.close();
}
}
return back;
}
/**
* 根據傳進來信息來獲取對應的圖片,將圖片寫到http返回體中
*
* @param id 圖片的位置[1,length]
* @param width 裁剪圖片的寬
* @param height 裁剪圖片的高度
* @param pictureUrl 網絡圖片的地址
* @param response http返回體
*/
public static void cutToResponse(Integer id,Integer width, Integer height
,String pictureUrl,HttpServletResponse response) throws Exception{
try{
//獲取數據
HttpByteArray data = GetUrlData.getByte(pictureUrl);
BufferedImage image = ImageIO.read(new ByteArrayInputStream(data.getData()));
//判斷是否圖片
if (image==null){
throw new Exception("目標圖片獲取失敗!-pictureUrl-"+pictureUrl);
}
//獲取圖片信息
Integer pictureWidth=image.getWidth();
Integer pictureHeight=image.getHeight();
Integer pictureX=0;
Integer pictureY=0;
Integer index=0;
//判斷傳入的長度是否滿足裁剪
while (pictureX + width <= pictureWidth && pictureY + height <= pictureHeight) {
index++;
//判斷是否是獲取到的目標位置
if (index.equals(id)){
BufferedImage subImage = image.getSubimage(pictureX, pictureY, width, height);
//寫入文件
ImageIO.write(subImage, "jpg", response.getOutputStream());
return;
}
//偏移下一個位置
pictureX += width;
//判斷下一個要裁剪的,是否跳轉下一行
if (pictureX + width > pictureWidth && pictureY + height <= pictureHeight) {
pictureX = 0;
pictureY += height;
}
}
//沒有匹配到對應圖片的話拋出異常
throw new Exception("目標圖片獲取失敗!-pictureWidth-"+pictureWidth
+"-pictureHeight-"+pictureHeight+"cutWidth"+width
+"cutHeight"+height+"-index-"+index+"-id-"+id);
}catch (Exception e){
throw e;
}
}
/**
* 獲取文件的後綴名
*
* @param fileName 一個有後綴名的文件名(.*)
* @return
* @throws Exception
*/
private static String getFormatName(String fileName) throws Exception {
try {
//判斷文件名是否合法
if (StringUtils.isEmpty(fileName)) {
throw new Exception("獲取文件後綴名傳入,文件名爲空!");
}
//尋找文件後綴名
String regex = "\\.[a-zA-Z]+";
String replace = "\\.";
//只獲取最後一個後綴名
List<String> find = RegexFinder.getAllToListByReplaceEmpty(regex, fileName, replace);
//判斷是否匹配後綴名
if (find == null || find.size() == 0) {
throw new Exception("獲取文件後綴名失敗!文件沒有後綴名-fileName-" + fileName);
} else {
//文件以最後一個後綴名有效
return find.get(find.size() - 1);
}
} catch (Exception e) {
throw e;
}
}
}
2、正則輔助
/**
* 獲取正則匹配全部可能,到list數據,替換字符中得數據
* @param regex
* @param info
* @param replace
* @return
*/
public static List<String> getAllToListByReplaceEmpty(String regex,String info
, String replace) {
//數據安全校驗
if(StringUtils.isEmpty(regex)||StringUtils.isEmpty(info)){
return null;
}
List<String> back=new ArrayList<String>();
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(info);
while (matcher.find()){
back.add(matcher.group().replaceAll(replace,""));
}
return back;
}
3、網絡數據獲取
/**
* 根據傳入的url將結果以byte[]的數據返回
* @param url 需要請求的url
* @return
* @throws Exception
*/
public static HttpByteArray getByte(String url)throws Exception{
//當傳入的url返回不爲空的時候,讀取數據
InputStream input;
if(StringUtils.isNotBlank(url)){
try{
URLConnection connection=GetUrlData.getConnection(url,null);
//獲取請求回來的信息
//獲取頭信息
String type= connection.getHeaderField("Content-Type");
input = connection.getInputStream();
//解決inputStream.available(),讀取圖片不完整問題
//此方法在內部緩衝輸入,因此不需要使用BufferedInputStream。
byte[] data = IOUtils.toByteArray(input);
//關閉讀取流
input.close();
//拼接返回數據獲取
return new HttpByteArray(type,data);
}catch(Exception e){
throw new Exception("讀取Url數據失敗:"+url,e);
}
}
return null;
}
/**
* 根據傳入的數據獲取URLConnection對象
* @param url
* @param mapArgs
* @return
* @throws Exception
*/
public static URLConnection getConnection(String url, Map<String,String> mapArgs)throws Exception{
//設置請求的頭信息
URL urlInfo = new URL(url);
URLConnection connection = urlInfo.openConnection();
//設置傳入的頭信息
if (mapArgs!=null){
for(String key:mapArgs.keySet()){
connection.addRequestProperty(key,mapArgs.get(key));
}
}
//設置默認頭信息
connection.addRequestProperty("Host", urlInfo.getHost());
connection.addRequestProperty("Connection", "keep-alive");
connection.addRequestProperty("Cache-Control", "max-age=0");
connection.addRequestProperty("Upgrade-Insecure-Requests", "1");
//表示用戶不願意目標站點追蹤用戶個人信息。
connection.addRequestProperty("DNT", "1");
//強制要求緩存服務器在返回緩存的版本之前將請求提交到源頭服務器進行驗證。
connection.addRequestProperty("Pragma", "no-cache");
connection.addRequestProperty("Accept", "*/*");
connection.addRequestProperty("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.140 Safari/537.36");
connection.addRequestProperty("Referer", "http://"+urlInfo.getHost());
connection.connect();
return connection;
}
4、網絡數據封裝HttpByteArray
public class HttpByteArray {
private String contentType;//http返回類型
private byte[] data;//http返回字節數組
public HttpByteArray(String contentType, byte[] data) {
this.contentType = contentType;
this.data = data;
}
//get,set略
}
測試代碼
思路1測試:將圖片分割成等分保存成文件
package tool;
import org.junit.Test;
import static org.junit.Assert.*;
/**
* Created by yuyu on 2018/3/28.
* 測試裁剪圖片
*/
public class CropPictureBuilderTest {
@Test
public void testCut()throws Exception {
System.out.println(CropPictureBuilder.cutToFile("E:/vs/picture.jpg",40,40));
}
}
測試結果截圖:
思路2測試:將獲取圖片寫成接口形式
package web;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import tool.CropPictureBuilder;
import javax.servlet.http.HttpServletResponse;
/**
* Created by yuyu on 2018/3/29.
* 用戶相關
*/
@Controller
@RequestMapping("/User")
public class UserController {
private final Logger logger= LoggerFactory.getLogger(this.getClass());
/**
* 根據傳入的參數獲取特定位置用戶頭像
*
* 將目標的用戶圖片讀入圖片流,然後裁剪對應的圖片,寫入http返回中
*/
@RequestMapping(value="/{id}/{width}/{height}/Picture",
method = RequestMethod.GET)
public void getUserImage(@PathVariable("id") Integer id
,@PathVariable("width") Integer width
,@PathVariable("height") Integer height
, HttpServletResponse response){
try{
String Url="http://www.dobeone.com/user.jpg";
//設置瀏覽器緩存
response.setHeader("Cache-Control", "max-age=31536000");
CropPictureBuilder.cutToResponse(id,width,height,Url,response);
}catch (Exception e){
response.setStatus(404);
e.printStackTrace();
}
}
}
測試結果截圖:
總結
1、獲取部分圖片的時候,以上程序讀取圖片的順序時時按行級優先,以及從上至下的
2、生成用戶圖片鏈接地址的時候,記得不要超過圖片的讀取範圍