很久之前寫了這樣一篇博客,就很久沒有更新博客了,後面陸陸續續有朋友在下面評論,詢問一些問題。由於之前寫的實在是時代久遠,索性直接重構了一遍,重構過後的系統界面如下:
開發環境:
- windows
- eclipse
- mysql
體驗區:https://nicecoder.cn/newspublishsystem
管理員界面:https://nicecoder.cn/newspublishsystem/login
【首頁】
首頁還沿用之前的樣式,稍作調整。提供按條件查詢新聞,可以根據新聞類型、關鍵字、類型+關鍵字進行檢索。
- 加載方式採用了無限下拉分頁加載,判斷滾動條到了底部:$(window).scrollTop() == $(document).height() - $(window).height();
$(function(){
var flag = 0;
$(window).scroll(function(){
if ($(window).scrollTop() == $(document).height() - $(window).height()) {
var pageCount = $("input[name='pageCount']").val();
var pageNo = $("input[name='pageNo']").val();
var keyWord = $("input[name='keyWord']").val();
var type = $("input[name='type']").val();
if(pageNo < pageCount){
$.ajax({
url:"coreServlet?bizCode=7",
dataType:"json",
data:{'keyword':keyWord, 'type':type, 'pageNo':parseInt(pageNo)+1},
success:function(data){
var html = "";
$.each(data.newsList, function(i,val){
html += '<div class="new"> ';
html += ' <div class="content row">';
html += ' <div class="imgdiv">';
html += ' <a href="coreServlet?bizCode=4&id='+val.id+'"><img src="'+val.img+'"/></a>';
html += ' </div>';
html += ' <div class="text">';
html += ' <a href="coreServlet?bizCode=4&id='+val.id+'"><h4>"'+val.title+'</h4></a>';
html += ' <p class="p1">"'+val.pudate+'-"'+val.type+'-"'+val.author+'-閱"'+val.click+'</p>';
html += ' <p class="p2">"'+val.content+'</p>';
html += ' </div>';
html += ' </div>';
html += '</div> ';
});
$(".new").last().after(html);
$("input[name='pageNo']").val(parseInt(pageNo) + 1);
}
});
}else if(pageNo == pageCount && flag == 0){
flag ++;
var html = '<p style="text-align:center; color:#FF8C69; font-size:14px; margin-top:10px; margin-bottom:10px;">-沒有更多了-</p>';
$(".new").last().after(html);
}
}
});
});
- 時間格式化:採用自定義jstl標籤。
- WEB-INF下面創建mytag.tld
<?xml version="1.0" encoding="UTF-8"?> <taglib xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-jsptaglibrary_2_0.xsd" version="2.0"> <tlib-version>1.0</tlib-version> <short-name>myTag</short-name> <function> <description>date change</description><!-- 對這個EL方法的描述 --> <name>formatDate</name><!-- 調用EL方法的名稱 --> <function-class>cn.nicecoder.util.UFunction</function-class> <function-signature> java.lang.String formatDate(java.lang.String) </function-signature> <example>${myTag:formatDate(str)}</example><!-- 例如 --> </function> </taglib>
- 創建類,創建方法
public class UFunction { /** * 轉換格式"年-月-日" * @param dateTime * @return */ public static String formatDate(String dateTime){ if(StringUtil.isNotEmpty(dateTime)){ return dateTime.substring(0,4) + "-" +dateTime.substring(4,6) + "-" + dateTime.substring(6,8); } return dateTime; } }
- 調用方式,jsp裏面調用如下
<%@ taglib uri="/WEB-INF/mytag.tld" prefix="myTag" %> //頂部添加
-
<p class="p1">${myTag:formatDate(news.pudate)} - <span style="color: #20B2AA;">${news.type}</span> - ${news.author} - 閱${news.click}</p>
【詳情頁】
詳情頁和下面的管理員頁面都採用了我最近發現的一個好用的UI框架layui,這個框架有自己的社區,而且持續更新中,感興趣的可以百度layui。拋棄了在我看來只適合做後端的bootstrap。提供了發表評論和點贊評論功能。評論評論和點贊文章功能也有預留,type傳值不同罷了,但是ui要稍作修改,暫時還沒想好,後續可能會加上。評論的頭像顯示,再加入用戶表之後也可以完善進去。
- layui富文本編輯器,功能不是很強,但是我更看中它的簡潔和外觀,鵝且持續更新中,用法見官網。
- 錨點定位,滑下來再滑上去手會很痛。使用起來很簡單。
<a name="mao"></a>//點擊下面的a標籤會跳轉到這裏
<a href="#mao" ><i class="layui-icon layui-icon-face-smile"></i></a>//圖中的top箭頭
- ajax異步提交評論
function discuss(type, id){ $("#dis-btn").attr("disabled","true"); var content = layedit.getContent(index); $.ajax({ dataType:'json', data :{'content':content, 'type':type, 'id':id}, url :"coreServlet?bizCode=5", async : true, success :function(data){ location.href = "coreServlet?bizCode=4&id="+ id; } }); }
- 獲取請求的IP,得到IP的地區信息,這兩個就從別的地方借來用用,這裏記錄一下,有點長但是很好用。
public class IPUtil { public static String getIpAddress(HttpServletRequest request) { String ip = request.getHeader("x-forwarded-for"); if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { ip = request.getHeader("Proxy-Client-IP"); } if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { ip = request.getHeader("WL-Proxy-Client-IP"); } if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { ip = request.getHeader("HTTP_CLIENT_IP"); } if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { ip = request.getHeader("HTTP_X_FORWARDED_FOR"); } if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { ip = request.getRemoteAddr(); } return ip; } }
public class AddressUtil{ /** * * @param content * 請求的參數 格式爲:name=xxx&pwd=xxx * @param encoding * 服務器端請求編碼。如GBK,UTF-8等 * @return * @throws UnsupportedEncodingException */ public static String getAddresses(String content, String encodingString)throws UnsupportedEncodingException { // 這裏調用pconline的接口 String urlStr = "http://ip.taobao.com/service/getIpInfo.php"; // 從http://whois.pconline.com.cn取得IP所在的省市區信息 String returnStr = getResult(urlStr, content, encodingString); if (returnStr != null) { // 處理返回的省市區信息 String[] temp = returnStr.split(","); if(temp.length<3){ return "0";//無效IP,局域網測試 } String region = (temp[5].split(":"))[1].replaceAll("\"", ""); region = decodeUnicode(region);// 省份 String country = ""; String area = ""; // String region = ""; String city = ""; String county = ""; String isp = ""; for (int i = 0; i < temp.length; i++) { switch (i) { case 1: country = (temp[i].split(":"))[2].replaceAll("\"", ""); country = decodeUnicode(country);// 國家 break; case 3: area = (temp[i].split(":"))[1].replaceAll("\"", ""); area = decodeUnicode(area);// 地區 break; case 5: region = (temp[i].split(":"))[1].replaceAll("\"", ""); region = decodeUnicode(region);// 省份 break; case 7: city = (temp[i].split(":"))[1].replaceAll("\"", ""); city = decodeUnicode(city);// 市區 break; case 9: county = (temp[i].split(":"))[1].replaceAll("\"", ""); county = decodeUnicode(county);// 地區 break; case 11: isp = (temp[i].split(":"))[1].replaceAll("\"", ""); isp = decodeUnicode(isp); // ISP公司 break; } } return region; } return null; } /** * @param urlStr * 請求的地址 * @param content * 請求的參數 格式爲:name=xxx&pwd=xxx * @param encoding * 服務器端請求編碼。如GBK,UTF-8等 * @return */ private static String getResult(String urlStr, String content, String encoding) { URL url = null; HttpURLConnection connection = null; try { url = new URL(urlStr); connection = (HttpURLConnection) url.openConnection();// 新建連接實例 connection.setConnectTimeout(2000);// 設置連接超時時間,單位毫秒 connection.setReadTimeout(2000);// 設置讀取數據超時時間,單位毫秒 connection.setDoOutput(true);// 是否打開輸出流 true|false connection.setDoInput(true);// 是否打開輸入流true|false connection.setRequestMethod("POST");// 提交方法POST|GET connection.setUseCaches(false);// 是否緩存true|false connection.connect();// 打開連接端口 DataOutputStream out = new DataOutputStream(connection.getOutputStream());// 打開輸出流往對端服務器寫數據 out.writeBytes(content);// 寫數據,也就是提交你的表單 name=xxx&pwd=xxx out.flush();// 刷新 out.close();// 關閉輸出流 BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream(), encoding));// 往對端寫完數據對端服務器返回數據 // ,以BufferedReader流來讀取 StringBuffer buffer = new StringBuffer(); String line = ""; while ((line = reader.readLine()) != null) { buffer.append(line); } reader.close(); return buffer.toString(); } catch (IOException e) { e.printStackTrace(); } finally { if (connection != null) { connection.disconnect();// 關閉連接 } } return null; } /** * unicode 轉換成 中文 * * @author fanhui 2007-3-15 * @param theString * @return */ public static String decodeUnicode(String theString) { char aChar; int len = theString.length(); StringBuffer outBuffer = new StringBuffer(len); for (int x = 0; x < len;) { aChar = theString.charAt(x++); if (aChar == '\\') { aChar = theString.charAt(x++); if (aChar == 'u') { int value = 0; for (int i = 0; i < 4; i++) { aChar = theString.charAt(x++); switch (aChar) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': value = (value << 4) + aChar - '0'; break; case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': value = (value << 4) + 10 + aChar - 'a'; break; case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': value = (value << 4) + 10 + aChar - 'A'; break; default: throw new IllegalArgumentException( "Malformed encoding."); } } outBuffer.append((char) value); } else { if (aChar == 't') { aChar = '\t'; } else if (aChar == 'r') { aChar = '\r'; } else if (aChar == 'n') { aChar = '\n'; } else if (aChar == 'f') { aChar = '\f'; } outBuffer.append(aChar); } } else { outBuffer.append(aChar); } } return outBuffer.toString(); } // 測試 public static void main(String[] args) { AddressUtil addressUtils = new AddressUtil(); String ip = "0:0:0:0:0:0:0:1"; String address = ""; try { address = addressUtils.getAddresses("ip=" + ip, "utf-8"); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } System.out.println(address); } }
【管理員】
管理員頁面依然採用的layui,實現了對新聞的發佈,編輯,詳情,刪除。
- 點擊箭頭分頁和首頁採用的同一個js方法,傳的參數不同。
- 類型轉義可做成數據字典,通過自定義jstl標籤進行轉義顯示。
- 上傳圖片,點擊上傳圖片按鈕和文章中插入圖片採用同一個接口。採用的servlet3.0。好處就是寫servlet不用配置web.xml,可以像springMVC一樣使用註解。
- @WebServlet(name="imageUpload", urlPatterns="/imageUpload") 替代web.xml配置方式。
- @MultipartConfig 提供HttpServletRequest對文件上傳的支持。通過Part p = req.getPart("name");name是input標籤的name,如果name是不確定的,那麼久用下面這種獲取集合的方式獲取到Part集合,再遍歷之。通過下列一系列操作就可以把本地文件、圖片上傳到服務器。然後就可以被頁面引用啦。
- 圖片壓縮是必要的,如果上傳的圖片過大,不僅佔網速,佔空間,在顯示的時候也會出現問題,img會撐破div跑到外面去。後端處理方式通過Thumbnails這個jar包提供一些方法進行壓縮,可以判斷文件大小,也可以判斷寬高,我採用的是寬高。同款jar包下載就在我的資源。
前端處理方式(發佈新聞的時候,我是通過複製別的地方的新聞,圖片自然就是引用的網上的地址,沒有經過後臺上傳。沒 有被壓縮,顯示會出問題。解決方法:img {max-width:762px;overflow:hidden;})
package cn.nicecoder.servlet;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.Collection;
import java.util.Date;
import javax.imageio.ImageIO;
import javax.servlet.ServletException;
import javax.servlet.annotation.MultipartConfig;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.Part;
import net.coobird.thumbnailator.Thumbnails;
import net.sf.json.JSONObject;
/**
* 上傳文件servlet
*-------------------------------
* @author longtian
* @date 2018年4月23日下午11:09:00
* @description nicecoder.cn
*-------------------------------
*/
@WebServlet(name="imageUpload", urlPatterns="/imageUpload")
@MultipartConfig
public class UploadImageServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
super.doPost(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.setCharacterEncoding("UTF-8");
Collection<Part> parts = req.getParts();
JSONObject result = new JSONObject();
//Part p = req.getPart("name");
for (Part part : parts) {
String disposition = part.getHeader("content-disposition");
System.out.println("文件描述:" + disposition);
//文件名,文件類型,文件大小
String fileName = disposition.substring(disposition.lastIndexOf("=")+2, disposition.length()-1);
String fileType = part.getContentType();
long fileSize = part.getSize();
System.out.println("fileName: " + fileName);
System.out.println("fileType: " + fileType);
System.out.println("fileSize: " + fileSize);
//1.服務器保存文件路徑
String uploadPath = this.getServletConfig().getServletContext().getRealPath("/");
//2.文件夾按日期分類
String folder = new SimpleDateFormat("yyyyMMdd").format(new Date());
//3.拼接文件名
if(!new File(uploadPath + File.separator + folder).exists()) {
new File(uploadPath + File.separator + folder).mkdirs();
}
//重命名並寫入文件
fileName = new SimpleDateFormat("yyyyMMdd_HHmmSS").format(new Date()) + fileName.subSequence(fileName.indexOf("."), fileName.length());
part.write(uploadPath + folder + File.separator + fileName);
//返回存儲地址
String src = uploadPath + folder + File.separator + fileName;
BufferedImage bufferedImage = ImageIO.read(new File(src));
int width = bufferedImage.getWidth();
int height = bufferedImage.getHeight();
//圖片過大的壓縮一下
if(width > 762){
double scale = new BigDecimal((float)762/width).setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue();
Thumbnails.of(src).size(762,(int) (height * scale)).keepAspectRatio(false).toFile(src);
}
result.put("code", 0);
result.put("msg", "ok");
JSONObject subObject = new JSONObject();
subObject.put("src", folder + File.separator + fileName );
subObject.put("title", fileName);
result.put("data", subObject);
/*適應layui,所以返回如下格式{
"code": 0 //0表示成功,其它失敗
,"msg": "" //提示信息 //一般上傳失敗後返回
,"data": {
"src": "圖片路徑"
,"title": "圖片名稱" //可選
}
}*/
}
PrintWriter wp = resp.getWriter();
wp.write(result.toString());
wp.close();
}
}
【其他頁面】
管理員登錄頁面和權限頁面,後期可能會加404頁面,這裏就一筆帶過。
- 管理員登錄,管理員界面通關口令通過微信公衆號【在相思樹下】,回覆“1”獲取。這樣做是防止亂搞,非誠勿擾。公衆號也可能會突然分享一波源碼的鏈接,平時不會胡亂推送,可以放心關注啦~。
- 禁止訪問頁面,登錄密碼錯誤或者session過期都會跳到此頁面。
【數據庫設計】
系統共用了四張表,news<新聞表>,newsclass<新聞類別>,discuss<評論表>,agree<點贊表>
news表 | |||
名 | 類型 | 長度 | 備註 |
id | int | 10 | 序號 |
img | varchar | 100 | 封面 |
title | varchar | 50 | 標題 |
content | blob | 0 | 內容 |
type | varchar | 10 | 類型 |
author | varchar | 50 | 作者 |
pudate | varchar | 50 | 修改時間 |
click | varchar | 20 | 點擊量 |
discuss表 | |||
名 | 類型 | 長度 | 備註 |
id | int | 10 | 序號 |
type | varchar | 10 | 類型 |
discussid | varchar | 10 | 評論內容編號 |
content | blob | 0 | 內容 |
userid | varchar | 50 | 作者 |
pudate | varchar | 50 | 修改時間 |
agree | varchar | 20 | 點贊量 |
agree表 | |||
名 | 類型 | 長度 | 備註 |
id | int | 10 | 序號 |
type | varchar | 10 | 類型 |
agreeid | varchar | 10 | 點贊內容編號 |
userid | varchar | 50 | 作者 |
pudate | varchar | 50 | 修改時間 |
newsclass表 | |||
名 | 類型 | 長度 | 備註 |
id | int | 11 | 序號 |
clsname | varchar | 20 | 類型名 |
胡亂寫了一堆,可能有的朋友對該項目的源碼已經產生了一丟丟的興趣。那麼下面將放出源碼的獲取方式。雖然是個小項目,但是也投入了一定的精力,所以肯定不能無償獲取哈,希望能夠理解。同時項目中仍有許多不足之處,也請多多批評指正。
方式一:CSDN資源下載鏈接,此乃土豪獲取方式,僅需揮灑區區5個積分,便能立馬將文中所述同款源碼一波帶走,簡單而又粗暴。
方式二:通過公衆號【在相思樹下】,回覆“簽到”二字連續簽到3天,亦可獲得同款源碼下載鏈接。而且後期有一些小項目的源碼也能第一時間獲取得到。敬請關注。有的朋友在公衆號上私信我,說很着急,想立即獲得源碼的,請用方式一。說什麼也沒用,不聽~不聽~
公衆號的回覆是機器人回覆,我不一定能夠實時看見。BUG肯定是有的,有什麼問題先自行研究,如果實在不行就在下面留言,或者私信,有時候回覆較慢,請見諒。
2018-8-21 公衆號故障中。。。
附:鏈接: https://pan.baidu.com/s/1i5Ck80oY4rU7Y9cWxEaDuQ 密碼: v6cm 包含表結構和數據。圖片資源找不到了,自行修改。