本篇是在之前写的一篇《Android利用okhttp实现图片上传之安卓客户端请求》的博文基础上的讲解对应后台代码设计的文章,然后地址是https://blog.csdn.net/shaowanyunBLOG/article/details/82179271
关于java后台的话,在我之前没有了解的时候,是一点概念都没有的,甚至会觉得后台高深莫测,而且听别人讲服务器,还有各种各样的框架,什么SSH的,然后觉得神乎其神的,但是当前面五月份做学校毕设的时候,才开始向自己知识领域的这块盲区发起了正式挑战,庆幸这次没成为炮灰,坚持下来了,然后也算是对后台领域启蒙了。
好的,那么闲言少叙吧,直接进入正题,下面我就把我的后台的设计,当然算不上讲解,只能说是简单的给大家介绍一下,包括后台对客户端传过来的头像信息的bute数组怎么保存到数据库,然后在客户端有需要的时候再怎样给客户端返回,同时还有一个就是在客户端和后台之间请求往来时怎样实现和设计token的机制,包括我对这一机制的思考,大概本文就是这么一个思路吧。
首先需要说明一下的是,我的后台的开发环境是IntelliJ IDEA的一个破解版,没有使用Eclipse,所以包括后面我把所有的系列博客整理完了之后再整理我得demo源代码的时候,可能大家再服务端代码的导入可能还是对eclipse不太友好的,所以这个也是先行抱歉。但是我是因为比较习惯了AS的编码习惯,所以对JetBrains的产品风格都比较倾向。当然,也只是喜欢,并不是忠粉,要不然当初就不会想方设法弄破解版了, = = ,额,这题外话好像说远了,哈哈。
我的后台的话,就是简答的使用Servlet来对Post和Get请求进行处理的,然后也没有用一些很“神秘”的框架,可能对于新手来说,或是安卓有点基础,然后刚刚接触后台的人(比如说 我。。)来说会友好一些。
那么接下来还是一步步的去介绍我的后台的处理吧
因为我目前的处理是这样的,不管是客户端发起什么请求,都会先对token进行检查,查看有没有token,如果有的话,查看有没有过期,然后检查通过之后再进行相关的请求处理和应答。所以,我先分享一下关于我自己定义的token机制。
token的话,按照字面意思是令牌的意思,当客户端发起请求时,需要带上响应的token,然后验证本次的请求是否有效,算是一种保证安全的措施。token具有时效性,所以需要对它定义一个超时时间,当用户发起请求是,服务端获取token,拿到token的超时时间和当前的时间做对比,如果发现token超时,那么就需要提示用户token超时,然后请求被驳回,然后客户端提示用户重新登录,以获取新的token。
1. 然后下面就大致先来展示我的token验证机制,如下图所示:
具体流程设计如上图所示,当有新用户发起请求时,先对User表进行查询,先判断是否为已存在账户,如果账户不存在,那么判定是新用户,那么将跳过token的校验机制,当然如果这个时候用户去登录或者是更改密码或头像信息,结果肯定都是不成功的,如果用户发起的是注册请求,如果后续流程中用户注册成功,就生成一个token存入数据库中。
2. 下面看token的实体类定义:
public class TokenStatus {
private String username;
private String tokeninfo;
private long deadline;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getTokeninfo() {
return tokeninfo;
}
public void setTokeninfo(String tokeninfo) {
this.tokeninfo = tokeninfo;
}
public long getDeadline() {
return deadline;
}
public void setDeadline(long deadline) {
this.deadline = deadline;
}
public TokenStatus() {
}
public TokenStatus(String username, String tokeninfo, long deadline) {
this.username = username;
this.tokeninfo = tokeninfo;
this.deadline = deadline;
}
@Override
public String toString() {
return "TokenStatus{" +
"username='" + username + '\'' +
", tokeninfo='" + tokeninfo + '\'' +
", deadline=" + deadline +
'}';
}
}
TokenStatus中共有三个参数,其中username为用户名,tokeninfo为具体的token内容,deadline就是token的超时时间。
其中:
tokeninfo的定义:我这里调用api生成的一个uuid作为uuid。
String uuid = UUID.randomUUID().toString().replace("-", "");
deadline的定义:如下
long deadline = System.currentTimeMillis() + 86400000;
获取的当前时间的单位为毫秒,一天是86400秒,deadline在当前时间基础上再加上86400000,也就是设定当如果用户没有和后台进行信息交互超过24小时,定义token过期。
3. mysql数据库中对应的token相关数据库定义:
如图所示,这是在Navicat中截取的数据表图示。
然后我给出这个数据库的建表语句,如下:
/*
Navicat MySQL Data Transfer
Source Server : fleamarket
Source Server Version : 50560
Source Host : localhost:3306
Source Database : fleamarket
Target Server Type : MYSQL
Target Server Version : 50560
File Encoding : 65001
Date: 2018-08-29 15:03:09
*/
SET FOREIGN_KEY_CHECKS=0;
-- ----------------------------
-- Table structure for tokenstatus
-- ----------------------------
DROP TABLE IF EXISTS `tokenstatus`;
CREATE TABLE `tokenstatus` (
`username` varchar(100) NOT NULL,
`tokeninfo` varchar(500) NOT NULL,
`deadline` bigint(100) NOT NULL,
PRIMARY KEY (`username`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
有需要的可以CV然后执行就可以创建数据表了。
4.如果需要java对数据库操作,那么肯定是需要和数据库建立连接的,我这里定义了一个类去做相关的工作。
public class Dao {
private final static String DRIVER = "com.mysql.jdbc.Driver";
private final static String URL = "jdbc:mysql://localhost:3306/fleamarket";
private final static String USER = "root";
private final static String PWD = "root";
public Connection getCon() throws ClassNotFoundException, SQLException{
Class.forName(DRIVER);
Connection con = DriverManager.getConnection(URL,USER,PWD);
System.out.println(con);
return con;
}
public void closeAll(ResultSet rs , Statement st , Connection con) throws SQLException{
if(rs != null){
rs.close();
}
if(st != null){
st.close();
}
if(con != null){
con.close();
}
}
}
其实里面的内容都是大差不差的,我这里是用的本地数据库,然后如果大家是在云服务器上部署的数据库,那么把对应的URL的IP改成对应的云服务器的公网IP即可。
5.完成了上面的准备工作,那么下面就要看看具体的doPost请求处理逻辑了。
代码如下:
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
System.out.println("doPost");
request.setCharacterEncoding("UTF-8");
response.setContentType("application/json;charset=utf-8");//指定返回的格式为JSON格式
response.setCharacterEncoding("UTF-8");//setContentType与setCharacterEncoding的顺序不能调换,否则还是无法解决中文乱码的问题
BufferedReader reader = request.getReader();
String json = reader.readLine();
System.out.println("json is : " + json);
Gson gson = new Gson();
User user = gson.fromJson(json, User.class);
UserService service = new UserService();
int note = user.getNote();
int myResponse = 0;
ServletResponse servletResponse = new ServletResponse();
DaoImpl dao = new DaoImpl();
try {
boolean haveUser = dao.CheckUser(user.getUsername());//默认是新账户
if (!haveUser) {
//后续流程
} else {
//如果是老用户
if(dao.CheckTokenStatus(user.getUsername(),System.currentTimeMillis())){
//后续流程
}else{
dao.updateTokenStatus(user.getUsername(),12345678);
System.out.println("onPost:dao.CheckTokenStatus(user.getUsername(),System.currentTimeMillis()) is false && !NewUser");
//先根据username判断该用户存在不存在,如果存在的话,那么肯定就有token记录
//如果有token记录,但是token过期了,那么就返回特定的返回值 99
servletResponse.setResponseCode(99);
servletResponse.setResponseInfo("您的登录状态已失效,请重新登录");
PrintWriter out = response.getWriter();
Gson gson1 = new Gson();
out.write(gson1.toJson(servletResponse));
out.close();
}
}
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
如上图所示,在doPost方法中,先通过BufferReader获取客户端上传的json串,然后将json解析为一个User对象。然后后面就是定义了一些参数,其中DaoImpl是我的后台的数据库操作类的对象,本质上就是一个工具类。然后其他的参数对于token的介绍没有太大关系,等后面用到时再进行解释。
里面有自己定义的两个方法:CheckUser()和CheckTokenStatus(),下面给出代码:
public boolean CheckUser(String username) throws ClassNotFoundException, SQLException {
String str = "select * from marketuser where username = '" + username + "'";
Connection con = dao.getCon();
Statement statement = con.createStatement();
ResultSet rs = statement.executeQuery(str);
while (rs.next()) {
//已有账户,返回true
dao.closeAll(rs, statement, con);
return true;
}
//没有找到账户,返回false
dao.closeAll(rs, statement, con);
return false;
}
其中“dao”是数据库连接类Dao的对象:
Dao dao = new Dao();
在这个方法中,传入用户名,然后查询marketuser表,查询是否用户已存在,存在返回true,不存在返回false。
public boolean CheckTokenStatus(String username,long cuurenttime) throws ClassNotFoundException, SQLException {
String str = "select * from tokenstatus where username = '" + username + "'";
Connection con = dao.getCon();
Statement statement = con.createStatement();
ResultSet rs = statement.executeQuery(str);
//rs.next就是用户的token表的数据
long olddeadline = 0;
while (rs.next()) {
olddeadline = rs.getLong("deadline");
if(olddeadline == 12345678){
return false;
}else{
if(cuurenttime-olddeadline>0){
dao.closeAll(rs, statement, con);
return false;
}else{
System.out.println("cuurenttime-olddeadline is : "+(cuurenttime-olddeadline));
dao.closeAll(rs, statement, con);
return true;
}
}
}
return false;
}
在这个方法中,传入用户名和当前时间。先通过用户名查询出来对应的deadline,先比较deadline是否等于12345678,如果等于,返回false,也就是超时了,如果不等于,再用当前时间减去deadline,如果结果大于零,那么也就是超时了,返回false,两种情况都不满足,那么就返回true,即通过了token的验证。
至此,关于token的设计,就告一段落了,我这种设计方式我觉得肯定还是存在一定的设计漏洞的,毕竟第一次使用token机制,很多东西还需要继续学习。不过我觉得也还是有可取的地方的,所以我才分享给大家。
那么讲完token的验证机制,下面也该回归正题了,也就是后台对客户端上传的图片的处理。
首先还是从数据实体类来说,数据实体和安卓端相同,如下:
/**
* Created by 15927 on 2018/7/24.
* 设计:
* note是判断该组数据的使用用途,例如:0是表示注册;1表示登录;2表示更改密码
* icon是用户的头像,用户可以拍照或者是从相册中选取上传,并且做剪切和压缩处理
* username是数据库的主键,用户注册时会先判断是否昵称占用
* password是用户的登录密码,这里会把用户注册的密码进行md5加密之后保存为真正的password
*/
public class User {
private int note;
private byte[] icon;
private String username;
private String password;
public int getNote() {
return note;
}
public void setNote(int note) {
this.note = note;
}
public byte[] getIcon() {
return icon;
}
public void setIcon(byte[] icon) {
this.icon = icon;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public User() {
}
public User(int note, byte[] icon, String username, String password) {
this.note = note;
this.icon = icon;
this.username = username;
this.password = password;
}
@Override
public String toString() {
return "User{" +
"note=" + note +
", icon=" + Arrays.toString(icon) +
", username='" + username + '\'' +
", password='" + password + '\'' +
'}';
}
}
同样的,相对应的字段都有说明。
然后是我的数据库部分的设计,如下图
如图所示,和数据实体是一一对应的,然后对于icon的处理,我是定义的一个mediumblob类型的数据去保存byte[]的数据,然后从这里我的一个测试数据来看,password保存的数据就是我那篇讲安卓端的文章里面提到的,对密码明文进行MD5加盐算法之后的数据,原来的明文为(ShaoWY12345),可见,加完密之后就面目全非了。虽然从加密的类型来看,并不能完全高枕无忧,但是最起码比原来好多了。下面我把我的数据库结构代码导出分享出来,如下:
/*
Navicat MySQL Data Transfer
Source Server : fleamarket
Source Server Version : 50560
Source Host : localhost:3306
Source Database : fleamarket
Target Server Type : MYSQL
Target Server Version : 50560
File Encoding : 65001
Date: 2018-08-29 12:23:18
*/
SET FOREIGN_KEY_CHECKS=0;
-- ----------------------------
-- Table structure for marketuser
-- ----------------------------
DROP TABLE IF EXISTS `marketuser`;
CREATE TABLE `marketuser` (
`note` int(11) NOT NULL,
`icon` mediumblob,
`username` varchar(100) NOT NULL,
`password` varchar(100) NOT NULL,
PRIMARY KEY (`username`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
这个是直接从Navicat中导出的数据库结构,大家如果需要建表可以CV执行一下。
然后把这些准备工作搞定了之后,基本上正式工作就要开始了。
然后下面我把我的UserServlet的doPost()先拿出来,然后再一步步的去讲解里面的做法,代码如下:
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
System.out.println("doPost");
request.setCharacterEncoding("UTF-8");
response.setContentType("application/json;charset=utf-8");//指定返回的格式为JSON格式
response.setCharacterEncoding("UTF-8");//setContentType与setCharacterEncoding的顺序不能调换,否则还是无法解决中文乱码的问题
BufferedReader reader = request.getReader();
String json = reader.readLine();
System.out.println("json is : " + json);
Gson gson = new Gson();
User user = gson.fromJson(json, User.class);
UserService service = new UserService();
int note = user.getNote();
int myResponse = 0;
ServletResponse servletResponse = new ServletResponse();
DaoImpl dao = new DaoImpl();
try {
boolean haveUser = dao.CheckUser(user.getUsername());//默认是新账户
if (!haveUser) {
//该部分只会走一次
//如果是新用户
switch (note) {
case 0:
case 1:
//注册时先检查,是否该账号已被注册,如果已被注册,则提示用户
myResponse = service.register(user);
servletResponse.setResponseCode(myResponse);
if(myResponse == 1){
//这里,把用户的失效时间重新赋值(也就是用当前时间,替换掉过去的时间)
long deadline = System.currentTimeMillis() + 86400000*15;
System.out.println("default:deadline is : "+deadline);
String uuid = UUID.randomUUID().toString().replace("-", "");
TokenStatus tokenStatus = new TokenStatus();
tokenStatus.setUsername(user.getUsername());
tokenStatus.setTokeninfo(uuid);
tokenStatus.setDeadline(deadline);
dao.insertTokenStatus(tokenStatus);
servletResponse.setResponseInfo("注册成功");
}else if(myResponse == 2){
servletResponse.setResponseInfo("该用户已被注册");
}else{
servletResponse.setResponseInfo("注册失败");
}
PrintWriter out2 = response.getWriter();
Gson gson2 = new Gson();
out2.write(gson2.toJson(servletResponse));
out2.close();
break;
case 2:
case 3:
default:
break;
}
} else {
//如果是老用户
if(dao.CheckTokenStatus(user.getUsername(),System.currentTimeMillis())){
System.out.println("onPost:dao.CheckTokenStatus(user.getUsername(),System.currentTimeMillis()) is true || NewUser");
//如果不存在的话(首次登录),就按照之前的过程去处理
//如果有token记录,并且token也是有效的,那么也按照之前的过程去处理
switch (note) {
case 0:
case 1:
//注册时先检查,是否该账号已被注册,如果已被注册,则提示用户
myResponse = service.register(user);
servletResponse.setResponseCode(myResponse);
if(myResponse == 1){
//这里,把用户的失效时间重新赋值(也就是用当前时间,替换掉过去的时间)
long deadline = System.currentTimeMillis() + 86400000*15;
System.out.println("default:deadline is : "+deadline);
String uuid = UUID.randomUUID().toString().replace("-", "");
//如果是已存在用户
if(null!=(dao.getToken(user.getUsername())) && !(dao.getToken(user.getUsername())).equals("")){
dao.updateTokenStatus(user.getUsername(),deadline);
}else{
TokenStatus tokenStatus = new TokenStatus();
tokenStatus.setUsername(user.getUsername());
tokenStatus.setTokeninfo(uuid);
tokenStatus.setDeadline(deadline);
dao.insertTokenStatus(tokenStatus);
}
servletResponse.setResponseInfo("注册成功");
}else if(myResponse == 2){
servletResponse.setResponseInfo("该用户已被注册");
}else{
servletResponse.setResponseInfo("注册失败");
}
PrintWriter out2 = response.getWriter();
Gson gson2 = new Gson();
out2.write(gson2.toJson(servletResponse));
out2.close();
break;
case 2:
case 3:
default:
break;
}
}else{
dao.updateTokenStatus(user.getUsername(),12345678);
System.out.println("onPost:dao.CheckTokenStatus(user.getUsername(),System.currentTimeMillis()) is false && !NewUser");
//先根据username判断该用户存在不存在,如果存在的话,那么肯定就有token记录
//如果有token记录,但是token过期了,那么就返回特定的返回值 99
servletResponse.setResponseCode(99);
servletResponse.setResponseInfo("您的登录状态已失效,请重新登录");
PrintWriter out = response.getWriter();
Gson gson1 = new Gson();
out.write(gson1.toJson(servletResponse));
out.close();
}
}
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
代码是从上面讲解token的验证机制的基础上完善了“后续过程”,如代码中的加粗部分。其实这里本身还有登录、更改密码、更改头像等相关的操作,但是为了代码的简洁,所以我就直接把其他和注册无关的地方全部给删掉了。其中还是有几个参数需要解释一下的,前面讲token的时候有几个参数忽略了,但是这里是需要解释一下的:
UserService service = new UserService();
int note = user.getNote();
int myResponse = 0;
ServletResponse servletResponse = new ServletResponse();
UserService是定义的一个中间层,Servlet在处理请求需要操作数据库时,不直接操作数据库,而是访问UserService,然后UserService再访问DaoImpl数据库操作类,这样的话,相当于分层的概念,便于处理一些比较复杂的数据库功能,同时Servlet的代码也不会特别冗余。
note就是我定义的一个用来表示请求类型的一个标志位,比如说:0表示登录请求,1表示注册请求,2表示更改密码,3表示更换头像等
myResponse是定义的一个int型变量用来表征数据库操作结果的,拿用户注册来说,0表示注册失败,1表示注册成功,2表示拥护已被注册过等
ServletResponse 是我定义的一个用来封装服务器返回实体的一个实体类,当服务端响应结束时,对应的返回码和响应信息以及其他的信息将会被封装ServletResponse的对象中,然后再使用Gson的toJson方法变成json字符串,返回给客户端。
其定义和安卓端相同。代码如下:
public class ServletResponse {
private int responseCode;//服务器返回码
private String responseInfo;//服务器响应信息
private byte[] icon;//用户登录时需要返回给客户端的用户名信息
private String username;//用户登录时需要返回给客户端的用户的对应的头像信息
public int getResponseCode() {
return responseCode;
}
public void setResponseCode(int responseCode) {
this.responseCode = responseCode;
}
public String getResponseInfo() {
return responseInfo;
}
public void setResponseInfo(String responseInfo) {
this.responseInfo = responseInfo;
}
public byte[] getIcon() {
return icon;
}
public void setIcon(byte[] icon) {
this.icon = icon;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public ServletResponse() {
}
public ServletResponse(int responseCode, String responseInfo, byte[] icon, String username) {
this.responseCode = responseCode;
this.responseInfo = responseInfo;
this.icon = icon;
this.username = username;
}
@Override
public String toString() {
return "ServletResponse{" +
"responseCode=" + responseCode +
", responseInfo='" + responseInfo + '\'' +
", icon=" + Arrays.toString(icon) +
", username='" + username + '\'' +
'}';
}
}
然后可以看出来doPost()中新补充上来的代码有两部分是需要注意的,一方面就是每次请求成功之后都需要对token进行更新,当然新用户发请求并且成功时是需要执行插入token操作的。另一方面就是本文要讲的侧重点:图片信息byte[]的存库以及获取处理。
我先把关于更新和插入token的两个方法的具体代码给出来:
public int insertTokenStatus (TokenStatus tokenStatus) throws ClassNotFoundException, SQLException, UnsupportedEncodingException {
// TODO Auto-generated method stub
try {
System.out.println("insertTokenStatus : tokenStatus is : "+tokenStatus.toString());
Connection conn = dao.getCon();
String sql="insert into tokenstatus values ( ?, ?, ? )";
PreparedStatement ptmt=conn.prepareStatement(sql);
ptmt.setString(1,tokenStatus.getUsername());
ptmt.setString(2, tokenStatus.getTokeninfo());
ptmt.setLong(3,tokenStatus.getDeadline());
ptmt.execute();
return 1;
}catch(Exception e){
e.printStackTrace();
return 0;
}
}
public int updateTokenStatus (String username,long deadline) throws ClassNotFoundException, SQLException, UnsupportedEncodingException {
// TODO Auto-generated method stub
try {
Connection conn = dao.getCon();
String sql="update tokenstatus set deadline = ? where username = ?";
PreparedStatement ptmt=conn.prepareStatement(sql);
ptmt.setLong(1,deadline);
ptmt.setString(2, username);
ptmt.execute();
ptmt.close();
return 1;
}catch(Exception e){
e.printStackTrace();
return 0;
}
}
上面两个方法就是token相关的数据库操作了,因为操作比较明显,所以也就不多讲了。
那么接下来把重点放在对byte[]的数据库操作。当然,我觉得数据库对byte[]的处理方法有很多,我这里只讲我自己的方法。
private DaoImpl dao= new DaoImpl();
public int register (User user) throws ClassNotFoundException, SQLException, UnsupportedEncodingException {
if(dao.CheckUser(user.getUsername())){
return 2;
}else{
return dao.insertUser(user);
}
}
这个就是UserService中对用户注册定义的方法,其中会先判断是否用户已存在,使用的方法为CheckUser(),具体代码见上方讲token时展示的方法,当用户不存在时,也就是当注册用户是新用户时,执行对marketuser表的插入语句,具体代码如下:
public int insertUser(User user) throws ClassNotFoundException, SQLException, UnsupportedEncodingException {
// TODO Auto-generated method stub
try {
createFileWithByte(user.getIcon(),user.getUsername());
InputStream in = getImageByte("D://"+user.getUsername()+".png");
Connection conn = dao.getCon();
String sql="insert into marketuser values ( ?, ?, ?, ? )";
PreparedStatement ptmt=conn.prepareStatement(sql);
ptmt.setInt(1,user.getNote());
ptmt.setBinaryStream(2, in, in.available());
ptmt.setString(3,user.getUsername());
ptmt.setString(4,user.getPassword());
ptmt.execute();
return 1;
}catch(Exception e){
e.printStackTrace();
return 0;
}
}
可见,我这里对于byte[]的处理流程是这样的:
byte[] 转 file 转inputStream ,最后使用setBinaryStream方法将输入流存入数据库。
这里使用了两个方法:createFileWithByte()和getImageByte()方法。如下:
private void createFileWithByte(byte[] bytes,String filename) throws IOException {
// TODO Auto-generated method stub
/**
* 创建File对象,其中包含文件所在的目录以及文件的命名
*/
String fileName = filename+".png"; // 文件可以为任何名,但得注意:必须要有后缀名。
File file = new File("D://", fileName );
// 创建FileOutputStream对象
FileOutputStream outputStream = null;
// 创建BufferedOutputStream对象
BufferedOutputStream bufferedOutputStream = null;
try {
// 如果文件存在则删除
if (file.exists()) {
file.delete();
}
// 在文件系统中根据路径创建一个新的空文件
file.createNewFile();
// 获取FileOutputStream对象
outputStream = new FileOutputStream(file);
// 获取BufferedOutputStream对象
bufferedOutputStream = new BufferedOutputStream(outputStream);
// 往文件所在的缓冲输出流中写byte数据
bufferedOutputStream.write(bytes);
// 刷出缓冲输出流,该步很关键,要是不执行flush()方法,那么文件的内容是空的。
bufferedOutputStream.flush();
} catch (Exception e) {
// 打印异常信息
e.printStackTrace();
} finally {
// 关闭创建的流对象
if (outputStream != null) {
try {
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (bufferedOutputStream != null) {
try {
bufferedOutputStream.close();
} catch (Exception e2) {
e2.printStackTrace();
}
}
}
}
public static FileInputStream getImageByte(String infile) throws FileNotFoundException{
FileInputStream imageByte=null;
File file=new File(infile);
imageByte=new FileInputStream(file);
return imageByte;
}
这两个方法,分别是将byte[]转为特定格式文件和将文件转为InputStram的方法。
当然,本身可能还有其他的方法可以实现mysql存储byte[],但是我尝试了直接讲byte[]转为流,然后直接这么存,但是后来我查看日志,发现读取出来的byte[]和我存进去的byte[]二者有天壤之别,但是我现在还不理解为什么会出现那种情况,不过既然用File作为中转的方法可行,同时我还可以直观查看我上传的图片,我觉得就是好方法。当然如果大家还有很好的方法,也欢迎大家在评论区分享指正,提前拜谢。
最后的话,上面是存储图片过程的展示,我再把从数据库读取图片的过程展示出来吧。
public User selectUser(String username) throws ClassNotFoundException, SQLException {
User user = new User();
String str = "select * from marketuser where username = '" + username + "'";
Connection con = dao.getCon();
Statement statement = con.createStatement();
ResultSet rs = statement.executeQuery(str);
while (rs.next()) {
try {
user.setNote(rs.getInt("note"));
InputStream inputStream = rs.getBinaryStream("icon");
byte tempbyte[] = new byte[inputStream.available()];
inputStream.read(tempbyte);
user.setIcon(tempbyte);
user.setUsername(rs.getString("username"));
user.setPassword(rs.getString("password"));
}catch(Exception e){
e.printStackTrace();
}
}
dao.closeAll(rs, statement, con);
return user;
}
这个就是我这边目前测试可行的一个查询语句,并且把查询结果封装到User的对象中的过程,其实不难看出,基本上是属于存图片时的一个逆过程,先调用getBinaryStram方法并赋值给InputStram的对象,然后这个地方稍有不同的是直接将流写到了byte[]中,然后再调用user的setIcon方法,就实现了读取图片的功能了。然后安卓那边再显示的时候,可以先从byte[]转为bitmap,然后再从bitmap转为drawable,然后再调用ImageView的setImageDrawable方法即可,完整调用如下:
usericon.setImageDrawable(Util.Bitmaptodrawable(getActivity(), Util.Bytes2Bimap(Loginuser.getIcon())));
那么,至此,基本上服务端的代码也讲的差不多了,主要分为前后两部分,先是插题讲了自己实现的token的一个验证机制,然后再结合着安卓部分把对应的服务端对图片的存储和读取的方法给列举了一下,讲的东西有点多,希望对大家有帮助。尤其是对于那些和我一样的刚刚接触后台的朋友,希望可以给你们提供一些思考的方向,然后至于具体的源码的话,可能我需要一些时间去整理,因为最近一段时间工作上还是不能保证有充足的时间,但是我保证肯定会给大家提供源码的。包括安卓端和后台的都会提供,不过可能会晚些日子,希望大家不要见谅。然后如果对于我的讲解不是很懂的,有问题的,同样欢迎大家前来提问和赐教,我也会尽我所能进行解答。