java swing可预览图片的带缓存的JFileChooser_JAVA_编程开发_程序员俱乐部

中国优秀的程序员网站程序员频道CXYCLUB技术地图
热搜:
更多>>
 
您所在的位置: 程序员俱乐部 > 编程开发 > JAVA > java swing可预览图片的带缓存的JFileChooser

java swing可预览图片的带缓存的JFileChooser

 2014/8/29 18:36:28  dejish  程序员俱乐部  我要评论(0)
  • 摘要:最近在一个javaswingcs项目中有个需求,用户打开文件选择器时,需要预览图片,方便用户操作。用户的图片大部分都是100M一个的tif文件和一些几M一个的jpg文件,java实时压缩较费时,故需要缓存预览图。本类实现了预览图片生成的图片缓存到本地硬盘文件中,在项目的ImageCache目录下,用日期yyyy-MM-dd格式生成文件夹,当天的图片缓存文件存放在该文件中,程序每次启动加载缓存前,会将定义好的10天前的图片缓存删除,如果有些图片在被删除前3天有被再次访问
  • 标签:file 图片 Java Swing 缓存
最近在一个java swing cs项目中有个需求,用户打开文件选择器时,需要预览图片,方便用户操作。用户的图片大部分都是100M一个的tif文件和一些几M一个的jpg文件,java实时压缩较费时,故需要缓存预览图。
本类实现了预览图片生成的图片缓存到本地硬盘文件中,在项目的ImageCache目录下,用日期yyyy-MM-dd格式生成文件夹,当天的图片缓存文件存放在该文件中,程序每次启动加载缓存前,会将定义好的10天前的图片缓存删除,如果有些图片在被删除前3天有被再次访问,则该缓存文件会移动到当天缓存文件目录中。

首先,JFileChooser默认弹窗太小,SizedFileChooser类实现自定义大小和位置弹窗
class="java" name="code">import java.awt.Component;
import java.awt.Dimension;
import java.awt.HeadlessException;
import java.awt.Point;
import java.awt.Toolkit;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.File;
import javax.swing.JDialog;
import javax.swing.JFileChooser;

public class SizedFileChooser extends JFileChooser{
	private Point p;
	private Dimension d;

	public SizedFileChooser(File currentDirectory){
		super(currentDirectory);
	}

	public SizedFileChooser(String currentDirectoryPath){
		super(currentDirectoryPath);
	}

	public SizedFileChooser(){
		super();
	}

	public int showOpenDialog(Component parent) throws HeadlessException{
		return showOpenDialog(parent, null, 1000, 600);
	}

	public int showOpenDialog(Component parent, Point location, int width, int height) throws HeadlessException{
		if(location == null){
			Dimension dimesion = Toolkit.getDefaultToolkit().getScreenSize();
			location = new Point((dimesion.width - width) / 2, (dimesion.height - height) / 2);
		}
		p = location;
		d = new Dimension(width, height);
		return super.showOpenDialog(parent);
	}

	public int showOpenDialog(Component parent, Point location, Dimension size) throws HeadlessException{
		p = location;
		d = size;
		return super.showOpenDialog(parent);
	}

	public int showSaveDialog(Component parent, Point location, Dimension size) throws HeadlessException{
		p = location;
		d = size;
		return super.showSaveDialog(parent);
	}

	public int showDialog(Component parent, String approveButtonText, Point location, Dimension size){
		p = location;
		d = size;
		return super.showDialog(parent, approveButtonText);
	}

	public void getLastLocationAndSize(Point p, Dimension d){
		if(p != null){
			p.x = this.p.x;
			p.y = this.p.y;
		}
		if(d != null){
			d.width = this.d.width;
			d.height = this.d.height;
		}
	}

	public void onWindowClose(){

	}

	protected JDialog createDialog(Component parent) throws HeadlessException{
		final JDialog dlg = super.createDialog(parent);
		dlg.addComponentListener(new ComponentAdapter(){
			public void componentMoved(ComponentEvent e){
				p = dlg.getLocation();
			}

			public void componentResized(ComponentEvent e){
				d = dlg.getSize();
			}
		});
		if(p != null){
			dlg.setLocation(p);
		}
		if(d != null && d.width > 0 && d.height > 0){
			dlg.setSize(d);
		}
		dlg.addWindowListener(new WindowAdapter(){
			public void windowClosing(WindowEvent wevent){
				onWindowClose();
			}
		});
		return dlg;
	}
}

实现图片预览类
import java.awt.Component;
import java.awt.Dimension;
import java.awt.HeadlessException;
import java.awt.Point;
import java.awt.Toolkit;
import java.awt.image.BufferedImage;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.File;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import javax.imageio.ImageIO;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JFileChooser;
import javax.swing.SwingUtilities;
import javax.swing.filechooser.FileSystemView;
import javax.swing.filechooser.FileView;
import net.coobird.thumbnailator.Thumbnails;
import net.coobird.thumbnailator.geometry.Positions;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.time.DateUtils;
import com.anyfinding.bailushi.utils.CommUtils;
import com.anyfinding.bailushi.utils.Config;
import com.anyfinding.bailushi.utils.Const;
import com.anyfinding.bailushi.utils.IM4JUtils;
import com.anyfinding.bailushi.utils.ImageUtils;

public class PhotoSizedFileChooser extends SizedFileChooser{
	private static final ImageIcon defaultIcon = new ImageIcon(PhotoSizedFileChooser.class.getClass().getResource("/images/photo_img.png"));
	private static final Map<String, ImageCache> imageCacheMap = new ConcurrentHashMap<String, ImageCache>(4096);
	private ExecutorService executor = Executors.newFixedThreadPool(Const.PHOTO_THREAD);
	private ImagePreviewPanel preview = null;
	private Map<File, Boolean> statusMap = new HashMap<File, Boolean>();

	/**
	 * 窗口关闭时一些操作
	 */
	private void release(){
		if(executor != null){
			executor.shutdownNow();
			executor = null;
		}
		preview = null;
		//窗口关闭前,图片没有被预览完成,需要删除
		for(Entry<File, Boolean> entry : statusMap.entrySet()){
			if(!entry.getValue()){
				imageCacheMap.remove(entry.getKey());
			}
		}
		statusMap = null;
	}

	public void onWindowClose(){
		release();
	}

	public void approveSelection(){
		super.approveSelection();
		release();
	}

	public void cancelSelection(){
		super.cancelSelection();
		release();
	}

	/**
	 * 加载缓存
	 */
	public static void loadImageCache(){
		try{
			File file = new File("./ImageCache");
			if(file != null && file.exists()){
				new File(file.getAbsolutePath() + File.separator + CommUtils.dateToShortString(new Date())).mkdir();
				
				//删除过期缓存
				for(File f : file.listFiles()){
					if(!f.getName().contains("svn") && DateUtils.addDays(CommUtils.stringToDate(f.getName()), Config.imageCacheDay).before(new Date())){
						FileUtils.deleteDirectory(f);
					}
				}
				
				//加载缓存
				for(File f : file.listFiles()){
					if(!f.getName().contains("svn")){
						for(File cacheFile : f.listFiles()){
							if(!cacheFile.getName().contains("svn")){
								ImageIcon icon = new ImageIcon(ImageIO.read(cacheFile));
								imageCacheMap.put(cacheFile.getName(), new ImageCache(icon, cacheFile.getAbsolutePath()));
							}
						}
					}
				}
			}
		}catch(Exception e){
			e.printStackTrace();
		}
	}

	private static String getFileString(File file){
		String name = file.getAbsolutePath();
		name = name.replaceAll("/", "_");
		name = name.replaceAll("//", "_");
		name = name.replaceAll("\\\\", "_");
		name = name.replaceAll(":", "");
		return name.toLowerCase() + "_" + file.length() + "_" + file.lastModified();
	}

	public PhotoSizedFileChooser(String currentDirectoryPath){
		super(currentDirectoryPath);
		this.setFileSelectionMode(JFileChooser.FILES_ONLY);
		this.setMultiSelectionEnabled(true);
		this.setDialogTitle("请选择图片文件");
		this.setFileFilter(new PhotoFileFilter());
		preview = new ImagePreviewPanel();
		this.setAccessory(preview);
		this.addPropertyChangeListener(preview);
		this.addPropertyChangeListener(new PropertyChangeListener(){
			public void propertyChange(PropertyChangeEvent evt){
				//每次切换目录,结束图片预览线程
				if(JFileChooser.DIRECTORY_CHANGED_PROPERTY.equals(evt.getPropertyName())){
					if(preview != null){
						preview.clear();
					}
					if(executor != null){
						executor.shutdownNow();
						executor = null;
					}
					executor = Executors.newFixedThreadPool(Const.PHOTO_THREAD);
				}
			}
		});
		this.setFileView(new ThumbnailView());
	}

	public int showOpenDialog(Component parent) throws HeadlessException{
		Dimension d = Toolkit.getDefaultToolkit().getScreenSize();
		int width = d.width - 80;
		int height = d.height - 70;
		Point p = new Point((d.width - width) / 2, 20);
		return showOpenDialog(parent, p, width, height);
	}

	private class ThumbnailView extends FileView{
		public Icon getIcon(File file){
			if(ImageUtils.isImage(file)){
				String fileString = getFileString(file);
				ImageCache cache = imageCacheMap.get(fileString);
				ImageIcon icon = null;
				if(cache == null){
					icon = new ImageIcon(defaultIcon.getImage());
					imageCacheMap.put(fileString, new ImageCache(icon, file.getAbsolutePath()));
					statusMap.put(file, false);
					executor.submit(new ThumbnailIconLoader(icon, file));
				}else{
					icon = cache.getImageIcon();
					try{
						String cacheDate = cache.getCacheDate();
						//3天后就要过期的图片,如果还被再次访问,则把该缓存文件移动到当天缓存目录
						if(Config.imageCacheDay > 3 && CommUtils.isDate(cacheDate) && DateUtils.addDays(CommUtils.stringToDate(cacheDate), Config.imageCacheDay - 3).before(new Date())){
							File newFile = new File("./ImageCache" + File.separator + CommUtils.dateToShortString(new Date()) + File.separator + fileString);
							FileUtils.moveFile(new File(cache.getImagePath()), newFile);
							imageCacheMap.put(fileString, new ImageCache(icon, newFile.getAbsolutePath()));
						}
					}catch(Exception e){
						e.printStackTrace();
					}
				}
				return icon;
			}else{
				FileSystemView sv = FileSystemView.getFileSystemView();
				if(sv != null){
					return sv.getSystemIcon(file);
				}
				return super.getIcon(file);
			}
		}
	}

	private class ThumbnailIconLoader implements Runnable{
		private final ImageIcon icon;
		private final File file;
		private BufferedImage bgImage = null;
		private BufferedImage noPreviewImage = null;

		public ThumbnailIconLoader(ImageIcon i, File f){
			icon = i;
			file = f;
		}

		public void run(){
			try{
				if(bgImage == null){
					bgImage = ImageIO.read(this.getClass().getResource("/images/subject_bg.jpg"));
				}
				int size = defaultIcon.getIconWidth() > defaultIcon.getIconHeight() ? defaultIcon.getIconWidth() : defaultIcon.getIconHeight();
				BufferedImage image = IM4JUtils.fileToScaledBufferedImage(file, size, size);
				if(image != null){
					//bgImage是一个60x60的白色背景图,把预览的图片合并在一起,保存每个预览图片都是正方形,比较整齐
					image = Thumbnails.of(bgImage).scale(1).watermark(Positions.CENTER, image, 1.0f).outputQuality(0.5f).asBufferedImage();
					File outputfile = new File("./ImageCache" + File.separator + CommUtils.dateToShortString(new Date()) + File.separator + getFileString(file));
					ImageIO.write(image, "jpg", outputfile);
				}else{
					//预览失败的图片
					if(noPreviewImage == null){
						noPreviewImage = ImageIO.read(this.getClass().getResource("/images/no_preview2.png"));
					}
					image = noPreviewImage;
				}
				icon.setImage(image);
				statusMap.put(file, true);
				SwingUtilities.invokeLater(new Runnable(){
					public void run(){
						repaint();
					}
				});
			}catch(Exception e){
				e.printStackTrace();
			}
		}
	}
}


点击预览图,右图出大图预览
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.image.BufferedImage;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.File;
import javax.swing.ImageIcon;
import javax.swing.JFileChooser;
import javax.swing.JPanel;
import com.anyfinding.bailushi.utils.IM4JUtils;
import com.anyfinding.bailushi.utils.ImageUtils;

public class ImagePreviewPanel extends JPanel implements PropertyChangeListener{
	private ImageIcon icon;
	private Image image;
	private static final int ACCSIZE = 250;
	private Color bg;

	public ImagePreviewPanel(){
		setPreferredSize(new Dimension(ACCSIZE, -1));
		bg = getBackground();
	}

	public void clear(){
		image = null;
		repaint();
	}

	public void propertyChange(PropertyChangeEvent e){
		String propertyName = e.getPropertyName();
		if(propertyName.equals(JFileChooser.SELECTED_FILE_CHANGED_PROPERTY)){
			File file = (File)e.getNewValue();
			if(file == null){
				return;
			}
			if(ImageUtils.isImage(file)){
				BufferedImage bufferedImage = IM4JUtils.fileToScaledBufferedImage(file, ACCSIZE, ACCSIZE);
				if(bufferedImage != null){
					icon = new ImageIcon(bufferedImage);
				}else{
					icon = new ImageIcon(getClass().getResource("/images/no_preview.png"));
				}
				image = icon.getImage();
				scaleImage();
				repaint();
			}
		}
	}

	private void scaleImage(){
		int width = image.getWidth(this);
		int height = image.getHeight(this);
		if(height > width){
			height = (int)(height * (ACCSIZE * 1.0D / width));
			if(height > this.getSize().height){
				height = this.getSize().height;
			}
			width = ACCSIZE;
			image = image.getScaledInstance(width, height, Image.SCALE_FAST);
		}
	}

	public void paintComponent(Graphics g){
		g.setColor(bg);
		g.fillRect(0, 0, ACCSIZE, getHeight());
		g.drawImage(image, 2, 2, this);
	}

}


程序启动后,马上在线程中加载图片缓存
Base.cacheService.submit(new Runnable(){
				public void run(){
					PhotoSizedFileChooser.loadImageCache();
				}
			});



其它东西
因为图片大部分都是100M的tif和几M的jpg,所以图片压缩使用GraphicsMagick+im4j
有一些特别的tif,GraphicsMagick+im4j压缩出来后,生成BufferedImage会报错,
需要使用JAI包生成BufferedImage,JAI压缩大图片比GraphicsMagick+im4j慢6到12倍
图片合并使用的是Thumbnails的水印功能
  • 大小: 301.1 KB
  • 查看图片附件
发表评论
用户名: 匿名