本篇文章是 Robert 發表分享在JavaWorld 的文章

[網路] 自製網頁圖片下載器


由於嚮往 xlinx 大的 [網路]自製 抓DCVIEW攝影 照片 程式 簡單好用 [一按鈕搞定]  

剛下載來時,那時程式還是可以正常使用的,但是我的程度太差,完全看不懂,
最近想說再來研究一番,但程式卻已無法正常執行了,
研究了半天, 也不是搞得很清楚,於是自己寫寫看,
順便了解一下基本的原理,
當然無法像 xlinx 大的那樣可以自動登入與下載與識別圖片。
只有簡單的所見即所得的,看的到就抓得到。

由於寫程式的時候遇到很多問題,都是參考網站上的相關經驗。
且受益良多,於是野人獻曝地 把自己完成的東西也放上來分享與記錄自己。

使用&功能說明:
1.貼上目標網址,再按下[執行]即可下載。
2.預 設會在程式的所在資料夾下再建立一個 pic 的資料夾放置圖片。
3.預設下載 20 KB 以上的圖片,重複的檔案將被複寫。
4.點選 Table中每一行的fileName部分,可預覽下載完成的圖片。

PS:
1.本程式僅供 java 技術測試與分享,其餘目的皆與本人無關。
2.本程式僅在DCView上測試過,且這不是相簿下載器。
3.本人作業環境是 Eclipse + JRE System Library [ jdk1.6.0_05 ]

後記:
Duncan 大大到底是何許人也,怎麼什麼問題他都可以回答啊??
真神人也,要怎樣才能像他那樣強呢...

 


程式畫面.

 

my java

 

以下為原始碼 , 或按這裡下載



import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Container;
import java.awt.Image;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
 
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.border.EmptyBorder;
import javax.swing.event.ListSelectionEvent;
import javax.swing.table.DefaultTableModel;
 
//
public class picBox extends JFrame {
  JTable table = null ;
  JTextField txtURL = null;
  Container cp = null;
  JLabel lbPicPreview = null;
 
  final int atLeastSizeKB = 20 ;
  final String defaultFolder = "pic/" ;
  
  final double defaultW = 300.0 ;//自訂SizeW:
  final double defaultH = 300.0 ;//自訂SizeH:
  
  public static void main(String args[]) {
    new picBox();
  }
  
  picBox() {
    super("DCView WebPage Image Downloader");
    String testURL1="http://gallery.dcview.com/showGallery.php?id=8831";//For Test 風景
    String testURL2="http://gallery.dcview.com/showGallery.php?id=9309";//For Test 正妹
    //JTextField
    txtURL = new JTextField("");
    txtURL.setText(testURL2);
    
    //JButton: btnExe
    JButton btnExe = new JButton("執行") ;    
    btnExe.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
try {
  ActionExe(evt);
        } catch (InterruptedException e) {
          e.printStackTrace();
        }
}
   });
    
    //JButton: Exit
    JButton btnExit = new JButton("離開") ;
    btnExit.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
          System.exit(0);
}
   });
    //Preview Window
    lbPicPreview = new JLabel(changeImage("00.jpg"));
    
    try{
      //Table//以Model物件宣告建立表格的JTable元件
      table = new JTable(){
        public void valueChanged(ListSelectionEvent e){
          super.valueChanged(e); //呼叫基礎類別的valueChanged()方法, 否則選取動作無法正常執行
          if( table.getSelectedRow() == -1) return;//取得表格目前的選取列,若沒有選取列則終止執行方法
        }
      };    
      table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);         
      table.setSelectionBackground(Color.ORANGE);//設定選取背景顏色   
      table.setCellSelectionEnabled(true); //設定允許儲存格選取
      
      //取得處理表格資料的Model物件,建立關聯
      DefaultTableModel dtm = (DefaultTableModel)table.getModel(); //宣告處理表格資料的TableModel物件
      //Set ColumnHeader Name
      dtm.addColumn( "Item" );
      dtm.addColumn( "FileName" );
      dtm.addColumn( "Size(KB)" );
      
      //欄寬設定
      table.getColumnModel().getColumn(0).setPreferredWidth(40); //設定欄位的喜好寬度為40
      table.getColumnModel().getColumn(1).setPreferredWidth(270); //設定欄位的喜好寬度為270
      table.getColumnModel().getColumn(2).setPreferredWidth(70); //設定欄位的喜好寬度為70
      
      //註冊回應JTable元件的MouseEvent事件的監聽器物件
      table.addMouseListener(new MouseAdapter(){
        public void mouseClicked(MouseEvent e){
          int selRow = table.rowAtPoint(e.getPoint());//取得滑鼠點選位置所在之資料的列索引
          String Size = (String) table.getValueAt(selRow, 2);  //取得被點選資料列的第3欄的值
          if (Integer.parseInt(Size)> 0 ){
            String imgPath = defaultFolder + (String) table.getValueAt(selRow, 1);  //取得被點選資料列的第2欄的值
            lbPicPreview.setIcon((changeImage(imgPath)));
          }
        }
      });
      
    }catch ( Exception e){
      e.printStackTrace();
    }
    
    //setup main window
    Box bxRow1= new Box(BoxLayout.X_AXIS);
   bxRow1.add(Box.createHorizontalStrut(10));//Strut:固定長度的透明支架
   bxRow1.add(new JLabel("Target URL :"));
   bxRow1.add(Box.createHorizontalStrut(10));//Strut:固定長度的透明支架
   bxRow1.add(txtURL);
   bxRow1.add(Box.createHorizontalStrut(10));//Strut:固定長度的透明支架
   bxRow1.add(btnExe);
   bxRow1.add(Box.createHorizontalStrut(10));//Strut:固定長度的透明支架
   bxRow1.add(btnExit);
   bxRow1.add(Box.createHorizontalStrut(10));//Strut:固定長度的透明支架
    
   Box bxMix = new Box(BoxLayout.X_AXIS);
   bxMix.add(Box.createHorizontalStrut(10));//Strut:固定長度的透明支架
   bxMix.add(new JScrollPane(table));//放在 JScrollPane 內才有, Scrollbar
   bxMix.add(Box.createHorizontalStrut(10));//Strut:固定長度的透明支架
   bxMix.add(lbPicPreview);
   bxMix.add(Box.createVerticalStrut(10));//Strut:固定長度的透明支架
 
   //內容面板
    cp = getContentPane(); //取得內容面版
    cp.setLayout(new BorderLayout(10, 10));  //建立各區域水平、垂直間距為10的BorderLayout物件
    
    //將各按鈕控制項,加入版面的指定位置
    cp.add(bxRow1, BorderLayout.NORTH);
    cp.add(bxMix);
    
    ///設定根面版四周將使用寬度為5的空白框線
    getRootPane().setBorder(new EmptyBorder(5, 5, 5, 5));
    
    //設定視窗預設的關閉動作、視窗大小, 並顯示視窗  
    setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    setSize(750, 500);//pack();強迫視窗會變成最佳尺寸, 使 setSize 無效
    setVisible(true);
 
  }
 
  private void ActionExe(java.awt.event.ActionEvent evt) throws InterruptedException{
    this.setTitle("Analyzing and Downloading...");
    //reset Table
    DefaultTableModel dtm = (DefaultTableModel)table.getModel();
    for (int kk = dtm.getRowCount() - 1 ; kk >= 0 ; kk -- ){
      dtm.removeRow(kk);
    }
    
    String URL = txtURL.getText().trim();
    //讀入網頁原始碼
    if (readPage(URL) == -1){
      addMatch("ReadPage Error !!", 0 );
      return;
      };
      
    //分析網頁資料
    if (analyPage()== -1){
      addMatch("AnalyPage Error !!", 0 );
      return;
      };
    //這個 ActionExe 的 thread 比 analyPage 的 thead 早結束  
  }
  
  //換照片時,自動縮小
  private ImageIcon changeImage(String imgPath){
    ImageIcon img = new ImageIcon(imgPath) ;
    double imgW = img.getIconWidth();
    double imgH = img.getIconHeight();
    double scale = 1.0;
    if (imgW >= imgH){
      scale = defaultW / imgW ;
      img.setImage(img.getImage().getScaledInstance( (int) defaultW, (int) (imgH * scale) ,Image.SCALE_DEFAULT));
    }else{
      scale = defaultH / imgH ;
      img.setImage(img.getImage().getScaledInstance((int)(imgW * scale) , (int)defaultH,Image.SCALE_DEFAULT));
    }    
    return img;
  }
  
  // Add data to table.
  private void addMatch(String path, long size ) throws InterruptedException {
    DefaultTableModel dtm = (DefaultTableModel)table.getModel();
    dtm.addRow(new String[] {String.valueOf(dtm.getRowCount()+1), path, (size>0 ? setComma(size):"")});  
    if (path.indexOf("OK") > -1){this.setTitle("DCView WebPage Image Downloader");}
  }
  
  private String setComma( long size ) {
    int idx = 0;
    String s = String.valueOf(size);
    StringBuffer sb = new StringBuffer("") ;
    for(int k=s.length() ; k>0 ; k-- ){
      idx++;
      sb.append((idx != s.length()&& idx%3==0)? s.substring(k-1, k) + "," : s.substring(k-1, k));
    }
    return sb.reverse().toString();  
  }
 
  //分析網頁
  public int analyPage() {
   int c;
   final StringBuffer sb = new StringBuffer("");
   try{
     InputStreamReader isr = new InputStreamReader(new FileInputStream("WebPage.txt"),"Big5");
     while((c = isr.read()) != -1){ //Null, the end of file
       sb.append((char) c);
}
     //參考:http://www.javaworld.com.tw/jute/post/view?bid=29&id=42796&tpg=1&ppg=1&sty=0&age=0#42796
     //因為用 while 會佔住 cpu , 所以加一個 thead 來執行, 以讓畫面可在每加一筆後,立即更新
     //也就是 把 time-consuming 工作以獨立的 thread 來執行
     new Thread(){
       public void run() {
         String[] target = {".jpg",".bmp",".png"};//pass gif
         for ( int k=0 ; k < target.length ; k++){  
           int idx=0, start = 0 , tgtPos = 0 , httpPos = 0 ;
           StringBuffer sbPage = new StringBuffer(sb.toString().toLowerCase());
           
           while (true){
             tgtPos = sbPage.indexOf(target[k], start);
             if (tgtPos == -1 ){ break; }
//             if ( (idx++) >= 5 ){ break; }//for test
             
             //get target path
             httpPos = sbPage.lastIndexOf("http", tgtPos);
             String tgtURL = sbPage.substring(httpPos, tgtPos + 4).toString() ;  
             String fileName = tgtURL.substring(tgtURL.lastIndexOf("/")+1) ;
                       
             //檢查一些規則
             if ( checkRule(tgtURL , fileName) ){
               long fileSize = downloadPic(tgtURL);
               try {
                   if(fileSize > 0){
                     addMatch(fileName , fileSize );
                     Thread.sleep(10);
                   }
                }catch (InterruptedException e) {
                  e.printStackTrace();
                }  
             }
             //reset
             start = tgtPos + 4 ;
           }           
     }
         
         //Log for done
         try {
            addMatch("OKOK, Download Done !!", 0 );
          } catch (InterruptedException e) {
            e.printStackTrace();
          }
       }
     }.start();
   }catch (IOException e) {
      e.printStackTrace();
      return -1;
    }
   return 1;
   }
  
  //避掉分析錯誤或無法存檔的情況
  public boolean checkRule( String URL , String fileName ) {
 
    if ( !URL.substring(0, 4).equals("http") ){
      return false; //避掉一些相對路徑的圖片,這通常都很小
    
    }else if ( URL.indexOf('>') > 0 || URL.indexOf('<') > 0 ){
      //一些分析錯誤的情況
      //http://www.facebook.com/pages/DCView/402684696689?v=wall&ref=ts"><img title="facebook" alt="facebook" src="/i/facebook_20.png
      return false;
    
    }else if (fileName.length() > 170 ){      
    //Windows檔名長度限制260字元,但再加上本身資料夾路徑,D:\eclipse\WorkSpace\picBox\pic 所以就更少了
      return false;
    }
    
    //check double-file
    for (int k=0; k< table.getRowCount() ; k++){
      String downloadedFile = (String) table.getValueAt(k, 1);
      if ( downloadedFile.equals(fileName) ){//已下載, <img> 內 src= 和 alt= 裡面的圖片是一樣的
        return false;
      }
    }
    
    return true;
  }
  
  //讀入網頁原始碼
  public static int readPage( String strURL ) {
     int chunksize = 4096;
   byte[] chunk = new byte[chunksize];
   int count;
   try {
     URL pageUrl = new URL(strURL );
  
   // 讀入網頁(位元串流)
   BufferedInputStream bis = new BufferedInputStream(pageUrl.openStream());
   BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("WebPage.txt", false));
   while ((count = bis.read(chunk, 0, chunksize)) != -1) {
     bos.write(chunk, 0, count); // 寫入檔案
   }
   bos.flush();
   bos.close();
   bis.close();
  
   }catch (IOException e) {
     e.printStackTrace();
     return -1;
   }
   return 1;
   }
  
  
  //下載圖片
  public long downloadPic(String strURL){
    long fileSize = 0;
    FileOutputStream fos = null ;
    InputStream is = null ;
    try {
      System.out.println("Checking -> " + strURL);
      
      //為了避免 403 的錯誤,http://www.javaworld.com.tw/jute/post/view?bid=38&id=108431&tpg=1&ppg=1&sty=0&age=0#108431
      URL url = new URL(strURL);      
      URLConnection URLConn = (HttpURLConnection) url.openConnection();
      URLConn.setRequestProperty("User-agent","IE/6.0");
      fileSize = ( URLConn.getContentLength() / 1024) + 1 ; //Unit:KB , 自動進位^^
      
      if ( URLConn.getContentLength() > 0 && URLConn.getContentType().indexOf("text/html") >= 0) {        
        //未被授存取的圖片, 無權進入的網頁, 會回應出此類訊息 URLConn.getContentType()=> text/html; charset=iso-8859-1
        //例如 http://upload.dcview.com/u/gallery/201007/8191a51eea9df2e7defa401a36ded0b2.jpg
        return -1;
      }else if ( fileSize <= atLeastSizeKB ){
        //小於 atLeastSizeKB 不下載
        return -1;        
      }
      
      //check Folder for picture
      File f = new File(defaultFolder) ;
      if (!f.exists()){f.mkdirs();}
      f = null;
 
      //
      String filePath = defaultFolder + strURL.substring(strURL.lastIndexOf("/")+1) ;
      fos = new FileOutputStream(filePath, false);
      is = URLConn.getInputStream(); // OldScript:url.openStream();
      
      //==讀到 buffer[] 因為不能保證連線的品質,或是檔案較大的時候,也許會有lose
      int chunkSize = 1024 * 8;
      byte[] buf = new byte[chunkSize];
      int readLen;
      System.out.println("Downloading -> " + strURL);
      while ((readLen = is.read( buf, 0, buf.length)) != -1) {
       fos.write(buf, 0, readLen);
      }       
      is.close();      fos.close();
      
      //顯示已下載的圖片
     lbPicPreview.setIcon((changeImage(filePath)));
     lbPicPreview.repaint();
      
    } catch (MalformedURLException e) {
      fileSize = -1;
      e.printStackTrace();
    } catch (FileNotFoundException e) {
      fileSize = -1;
      e.printStackTrace();
    } catch (IOException e) {
      fileSize = -1;
      e.printStackTrace();
    } catch (Throwable e) {
      fileSize = -1;
      e.getMessage();
      e.printStackTrace();
    }finally{
      try {
        if (is !=null ){is.close();}
        if (fos !=null ){fos.close();}
      } catch (IOException e) {
        e.printStackTrace();
      }
    }
      
    return fileSize ;
  }
}
 
創作者介紹
創作者 羅伯特 的頭像
羅伯特

Thinking in Robert

羅伯特 發表在 痞客邦 留言(2) 人氣()


留言列表 (2)

發表留言
  • 羅伯特
  • 哈, 好久以前的東西了, 今天突然發現有留言, 真是嚇了一跳, 但其實已經忘了一大堆了~~
    希望對您有些幫助就好了~~