package example; import java.awt.*; import java.awt.event.*; import java.io.*; import java.sql.*; import org.postgresql.largeobject.*; /* * This example is a small application that stores and displays images * held on a postgresql database. * * Before running this application, you need to create a database, and * on the first time you run it, select "Initialise" in the "PostgreSQL" * menu. * * Important note: You will notice we import the org.postgresql.largeobject * package, but don't import the org.postgresql package. The reason for this is * that importing postgresql can confuse javac (we have conflicting class names * in org.postgresql.* and java.sql.*). This doesn't cause any problems, as * long as no code imports org.postgresql. * * Under normal circumstances, code using any jdbc driver only needs to import * java.sql, so this isn't a problem. * * It's only if you use the non jdbc facilities, do you have to take this into * account. * * Note: For PostgreSQL 6.4, the driver is now Thread safe, so this example * application has been updated to use multiple threads, especially in the * image storing and retrieving methods. */ public class ImageViewer implements ItemListener { Connection db; Statement stat; LargeObjectManager lom; Frame frame; Label label; // Label used to display the current name List list; // The list of available images imageCanvas canvas; // Canvas used to display the image String currentImage; // The current images name // This is a simple component to display our image public class imageCanvas extends Canvas { // holds the image private Image image; // holds the background buffer private Image bkg; // the size of the buffer private Dimension size; public imageCanvas() { image = null; } public void setImage(Image img) { image = img; repaint(); } // This defines our minimum size public Dimension getMinimumSize() { return new Dimension(400, 400); } public Dimension getPreferedSize() { return getMinimumSize(); } public void update(Graphics g) { paint(g); } /* * Paints the image, using double buffering to prevent screen flicker */ public void paint(Graphics gr) { Dimension s = getSize(); if (size == null || bkg == null || !s.equals(size)) { size = s; bkg = createImage(size.width, size.height); } // now set the background Graphics g = bkg.getGraphics(); g.setColor(Color.gray); g.fillRect(0, 0, s.width, s.height); // now paint the image over the background if (image != null) g.drawImage(image, 0, 0, this); // dispose the graphics instance g.dispose(); // paint the image onto the component gr.drawImage(bkg, 0, 0, this); } } public ImageViewer(Frame f, String url, String user, String password) throws ClassNotFoundException, FileNotFoundException, IOException, SQLException { frame = f; MenuBar mb = new MenuBar(); Menu m; MenuItem i; f.setMenuBar(mb); mb.add(m = new Menu("PostgreSQL")); m.add(i = new MenuItem("Initialise")); i.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { ImageViewer.this.init(); } } ); m.add(i = new MenuItem("Exit")); ActionListener exitListener = new ActionListener() { public void actionPerformed(ActionEvent e) { ImageViewer.this.close(); } }; m.addActionListener(exitListener); mb.add(m = new Menu("Image")); m.add(i = new MenuItem("Import")); ActionListener importListener = new ActionListener() { public void actionPerformed(ActionEvent e) { ImageViewer.this.importImage(); } }; i.addActionListener(importListener); m.add(i = new MenuItem("Remove")); ActionListener removeListener = new ActionListener() { public void actionPerformed(ActionEvent e) { ImageViewer.this.removeImage(); } }; i.addActionListener(removeListener); // To the north is a label used to display the current images name f.add("North", label = new Label()); // We have a panel to the south of the frame containing the controls Panel p = new Panel(); p.setLayout(new FlowLayout()); Button b; p.add(b = new Button("Refresh List")); b.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { ImageViewer.this.refreshList(); } } ); p.add(b = new Button("Import new image")); b.addActionListener(importListener); p.add(b = new Button("Remove image")); b.addActionListener(removeListener); p.add(b = new Button("Quit")); b.addActionListener(exitListener); f.add("South", p); // And a panel to the west containing the list of available images f.add("West", list = new List()); list.addItemListener(this); // Finally the centre contains our image f.add("Center", canvas = new imageCanvas()); // Load the driver Class.forName("org.postgresql.Driver"); // Connect to database db = DriverManager.getConnection(url, user, password); // Create a statement stat = db.createStatement(); // Also, get the LargeObjectManager for this connection lom = ((org.postgresql.Connection)db).getLargeObjectAPI(); // Now refresh the image selection list refreshList(); } /* * This method initialises the database by creating a table that contains * the image names, and Large Object OID's */ public void init() { try { //db.setAutoCommit(true); stat.executeUpdate("create table images (imgname name,imgoid oid)"); label.setText("Initialised database"); db.commit(); } catch (SQLException ex) { label.setText(ex.toString()); } // This must run outside the previous try{} catch{} segment //try { //db.setAutoCommit(true); //} catch(SQLException ex) { //label.setText(ex.toString()); //} } /* * This closes the connection, and ends the application */ public void close() { try { db.close(); } catch (SQLException ex) { System.err.println(ex.toString()); } System.exit(0); } /* * This imports an image into the database, using a Thread to do this in the * background. */ public void importImage() { FileDialog d = new FileDialog(frame, "Import Image", FileDialog.LOAD); d.setVisible(true); String name = d.getFile(); String dir = d.getDirectory(); d.dispose(); // now start the true importer Thread t = new importer(db, name, dir); //t.setPriority(Thread.MAX_PRIORITY); t.start(); } /* * This is an example of using a thread to import a file into a Large Object. * It uses the Large Object extension, to write blocks of the file to the * database. */ class importer extends Thread { String name, dir; Connection db; public importer(Connection db, String name, String dir) { this.db = db; this.name = name; this.dir = dir; } public void run() { // Now the real import stuff if (name != null && dir != null) { Statement stat = null; try { // fetch the large object manager LargeObjectManager lom = ((org.postgresql.Connection)db).getLargeObjectAPI(); db.setAutoCommit(false); // A temporary buffer - this can be as large as you like byte buf[] = new byte[2048]; // Open the file FileInputStream fis = new FileInputStream(new File(dir, name)); // Now create the large object int oid = lom.create(); LargeObject blob = lom.open(oid); // Now copy the file into the object. // // Note: we dont use write(buf), as the last block is rarely the same // size as our buffer, so we have to use the amount read. int s, t = 0; while ((s = fis.read(buf, 0, buf.length)) > 0) { t += s; blob.write(buf, 0, s); } // Close the object blob.close(); // Now store the entry into the table // As we are a different thread to the other window, we must use // our own thread stat = db.createStatement(); stat.executeUpdate("insert into images values ('" + name + "'," + oid + ")"); db.commit(); db.setAutoCommit(false); // Finally refresh the names list, and display the current image ImageViewer.this.refreshList(); ImageViewer.this.displayImage(name); } catch (Exception ex) { label.setText(ex.toString()); } finally { // ensure the statement is closed after us try { if (stat != null) stat.close(); } catch (SQLException se) { System.err.println("closing of Statement failed"); } } } } } /* * This refreshes the list of available images */ public void refreshList() { try { // First, we'll run a query, retrieving all of the image names ResultSet rs = stat.executeQuery("select imgname from images order by imgname"); if (rs != null) { list.removeAll(); while (rs.next()) list.addItem(rs.getString(1)); rs.close(); } } catch (SQLException ex) { label.setText(ex.toString() + " Have you initialised the database?"); } } /* * This removes an image from the database * * Note: With postgresql, this is the only way of deleting a large object * using Java. */ public void removeImage() { try { // // Delete any large objects for the current name // // Note: We don't need to worry about being in a transaction // here, because we are not opening any blobs, only deleting // them // ResultSet rs = stat.executeQuery("select imgoid from images where imgname='" + currentImage + "'"); if (rs != null) { // Even though there should only be one image, we still have to // cycle through the ResultSet while (rs.next()) { lom.delete(rs.getInt(1)); } } rs.close(); // Finally delete any entries for that name stat.executeUpdate("delete from images where imgname='" + currentImage + "'"); label.setText(currentImage + " deleted"); currentImage = null; refreshList(); } catch (SQLException ex) { label.setText(ex.toString()); } } /* * This displays an image from the database. * * For images, this is the easiest method. */ public void displayImage(String name) { try { // // Now as we are opening and reading a large object we must // turn on Transactions. This includes the ResultSet.getBytes() // method when it's used on a field of type oid! // db.setAutoCommit(false); ResultSet rs = stat.executeQuery("select imgoid from images where imgname='" + name + "'"); if (rs != null) { // Even though there should only be one image, we still have to // cycle through the ResultSet while (rs.next()) { canvas.setImage(canvas.getToolkit().createImage(rs.getBytes(1))); label.setText(currentImage = name); } } rs.close(); } catch (SQLException ex) { label.setText(ex.toString()); } finally { try { db.setAutoCommit(true); } catch (SQLException ex2) {} } } public void itemStateChanged(ItemEvent e) { displayImage(list.getItem(((Integer)e.getItem()).intValue())); } /* * This is the command line instructions */ public static void instructions() { System.err.println("java example.ImageViewer jdbc-url user password"); System.err.println("\nExamples:\n"); System.err.println("java -Djdbc.driver=org.postgresql.Driver example.ImageViewer jdbc:postgresql:test postgres password\n"); System.err.println("This example tests the binary large object api of the driver.\nBasically, it will allow you to store and view images held in the database."); System.err.println("Note: If you are running this for the first time on a particular database,\nyou have to select \"Initialise\" in the \"PostgreSQL\" menu.\nThis will create a table used to store image names."); } /* * This is the application entry point */ public static void main(String args[]) { if (args.length != 3) { instructions(); System.exit(1); } try { Frame frame = new Frame("PostgreSQL ImageViewer v7.0 rev 1"); frame.setLayout(new BorderLayout()); ImageViewer viewer = new ImageViewer(frame, args[0], args[1], args[2]); frame.pack(); frame.setLocation(0, 50); frame.setVisible(true); } catch (Exception ex) { System.err.println("Exception caught.\n" + ex); ex.printStackTrace(); } } }