/**
  * 使用键盘输入流,接收一大段文本数据,并输入一个图片的路径, 然后将这些数据保存在数据表中,数据表由用户自行创建。
  */
 package exercise17;import java.sql.Blob;
 import java.sql.Clob;
 import java.sql.Connection;
 import java.sql.DriverManager;
 import java.io.BufferedReader;
 import java.io.ByteArrayInputStream;
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.FileOutputStream;
 import java.io.InputStream;
 import java.io.InputStreamReader;
 import java.io.OutputStream;
 import java.sql.PreparedStatement;
 import java.sql.ResultSet;
 import java.sql.SQLException;
 import java.sql.Statement;public class Ex03 {
     public static final String DBDRIVER = "com.mysql.cj.jdbc.Driver";
     public static final String DBURL = "jdbc:mysql://localhost:3306/test";
     public static final String DBUSER = "root";
     public static final String DBPASS = "mysqladmin";
     public static void main(String[] args) throws Exception {
         // TODO Auto-generated method stub
         Connection conn = null;
         Class.forName(DBDRIVER);   // 加载驱动程序
         conn = DriverManager.getConnection(DBURL, DBUSER, DBPASS);
         createTable(conn);        // 创建名为info的数据表
         insertData(conn);        // 向数据库表中插入一行数据
         showPhoto(conn);          // 展示数据表中的数据内容,longtext类型数据打印在控制台,二进制大对象(longblob类型)输出到桌面的文件中
     }
     
     /**
      * 
      * @return 用户从键盘输入的字符串
      * @throws Exception
      */
     public static String getString() throws Exception {
         InputStreamReader isr = new InputStreamReader(System.in);  // 创建字符输入流实例,从键盘输入
         BufferedReader buf = new BufferedReader(isr);   // 创建字符输入流缓冲
         String temp = null;         // 保存用户输入的字符串
         temp = buf.readLine();      // 从字符输入流缓冲中读取一行字符
         return temp;
     }
     
     /**
      * @param 数据库的连接实例
      * @throws Exception
      * @function 向数据库中插入一行数据
      */
     public static void insertData(Connection conn) throws Exception {
         boolean flag = true;               //  定义一个标识符
         InputStream input = null;          // 定义一个字节输入流
         PreparedStatement pstmt = null;    // 数据库操作
         // 定义向数据库中插入数据的SQL语句
         String sql = "INSERT INTO info(note, photo) VALUES(?, ?)";
         pstmt = conn.prepareStatement(sql); // 获取数据库操作实例
         while (flag) {                     // 反复插入数据,直至标识符为false
             String text = getLargeText("请输入一个大文本数据:");   // 提示用户输入longtext类型的大文本数据
             byte[] b = text.getBytes();    // 将文本数据从字符串转换为byte类型的数组b
             input = new ByteArrayInputStream(b);  // 创建字节数组输入流,将字节数组b传入输入流
             pstmt.setAsciiStream(1, input);  // 设置SQL语句中的第一个?的值
             // 提示用户输入要保存的图片(longblob类型)路径,返回该文件的File实例
             File f = getPhotoDir("请输入一张图片的保存路径:", "路径错误,请重新输入:");  
             input = new FileInputStream(f);  // 创建文件的输入流
             pstmt.setBinaryStream(2, input); // 设置SQL语句中的第二个?的值
             pstmt.executeUpdate();           //  向数据库中的info表中插入数据
             System.out.println("是否继续更新数据?结束请输入‘N’:");
             String str = "";
             if ((str = getString()).equals("N") || str.equals("n")) {  // 如果用户输入了n(部分大小写),则将标识符改为false结束更新
                 flag = false;
             }
         }
     }
     
     /**
      * 
      * @param 提示信息
      * @return 字符串(用户输入的大文本数据内容)
      * @throws Exception
      */
     public static String getLargeText(String info) throws Exception {
         System.out.println(info);     // 打印提示信息
         StringBuffer buffer = new StringBuffer(); // 创建字符串缓冲
         String str = "";
      // 用户可多行输入字符串(删除字符串两端的空格被删除),直到输入end为止(建议用户输入图片文件的名称,这样在读取时可以利用note列获得文件名称)
         while (!(str = getString().trim()).equals("end")) { 
             buffer.append(str).append("\r\n");    // 每输入一行就加入到StringBuffer中
         }
         buffer.delete(buffer.length()-2, buffer.length());  // 当结束输入时,将最后面多余的换行符(\r和\n两个字符)删除
         return buffer.toString();     // 以字符串形式返回
     }
     
     /**
      * 
      * @param info:输入提示
      * @param err:错误提示
      * @return 用户输入的文件路径的File实例
      * @throws Exception
      */
     public static File getPhotoDir(String info, String err) throws Exception {
         System.out.println(info);    // 打印输入提示
         String path = getString();   // 获得文件路径的一个字符串
         File f = null;
         f = new File(path);          // 获得文件的File实例
         while (!f.exists() || !path.matches(".+\\.(jpg|png|jpeg|gif)")) {  // 判断文件是否存在,或文件格式(图片)是否正确
             System.err.println(err); // 如果判断结果为否,则打印错误提示
             path = getString();      //  重新输入
             f = new File(path);      // 重新创建File实例
         }
         return f;                    // 返回正确的文件File实例
     }
     
     /**
      * 
      * @param conn:数据库连接实例
      * @throws SQLException
      * @function 在数据库中创建数据表info(如果这个类型的表已经存在就不用重复创建了)
      */
     public static void createTable(Connection conn) throws SQLException {
         Statement stmt = null;    // 数据库操作
         stmt = conn.createStatement(); // 通过数据库连接得到数据库操作实例
         // 定义SQL语句,该语句的内容就是:创建数据表info(如果表不存在)。
         String sql = "create table if not exists info("
                 + "id int auto_increment primary key,"   // id列,自动增长、主键
                 + "note longtext,"                       // note列,大文本数据对象
                 + "photo longblob"                       // photo列,二进制大数据对象(图片)
                 + ");";
         stmt.execute(sql);        // 执行SQL语句,创建info表
     }
     
     /**
      * @function 展示数据表中的全部数据内容,longtext类型数据打印在控制台,二进制大对象(longblob类型)输出到桌面的文件中
      * @param conn: 数据库连接实例
      * @throws Exception
      */
     public static void showPhoto(Connection conn) throws Exception {
         PreparedStatement pstmt = null;
         ResultSet rs = null;
         String sql = "SELECT id, note, photo FROM info;";
         pstmt = conn.prepareStatement(sql);  // 获得数据库操作实例
         rs = pstmt.executeQuery();    // 实行SQL语句,得到查询的结果集
         while (rs.next()) {           // 展示结果集中的全部内容
             int id = rs.getInt("id"); // 获得id的值
             Clob clob = rs.getClob("note");  // 从结果集中获得大文本数据
             String note = clob.getSubString(1, (int)clob.length()); // 转换成字符串
             System.out.println("id:  " + id + "; note:  " + note);  // 输出id和note内容
             File f = new File("C:" + File.separator + "Users" + File.separator + "yeqin" + 
                 File.separator + "Desktop" + File.separator + note + ".gif"); // 得到输出文件的路径(在桌面)
             if (!f.exists()) {        // 如果输出的文件不存在,则新建一个
                 System.out.println("在" + f.toString() + "处新建一个文件!");
                 f.createNewFile();    // 新建一个文件
             }
             OutputStream output = new FileOutputStream(f);   // 得到文件的写入流
             Blob blob = rs.getBlob("photo");    // 从结果集中获得Blob实例
             byte[] b = blob.getBytes(1, (int)blob.length());  // 将Blob实例中的数据保存在byte类型数组中,注意Blob实例的起始位置为1,而不是0
             output.write(b);              // 将数组内容写入文件中
             output.close();
         }
         pstmt.close();
     }
 } /**
  * 程序运行结果:
      请输入一个大文本数据:
      red flower
     end
     请输入一张图片的保存路径:
     d:\photo\red flower.gif
     是否继续更新数据?结束请输入‘N’:
     y
     请输入一个大文本数据:
     HeloIcon_solid
     end
     请输入一张图片的保存路径:
     y
     路径错误,请重新输入:
     d:\photo\HelpIcon_solid.gif
     是否继续更新数据?结束请输入‘N’:
     y
     请输入一个大文本数据:
     image1
     end
     请输入一张图片的保存路径:
     d:\photo\image1.gif
     是否继续更新数据?结束请输入‘N’:
     n
     id:  1; note:  red flower
     在"C:\Users\yeqin\Desktop\red flower.gif"处新建一个文件!
     id:  2; note:  HeloIcon_solid
     在"C:\Users\yeqin\Desktop\HeloIcon_solid.gif"处新建一个文件!
     id:  3; note:  image1
     在"C:\Users\yeqin\Desktop\image1.gif"处新建一个文件!
  *
  */