1. 程式人生 > >office轉pdf(線上預覽)思路(java示例)

office轉pdf(線上預覽)思路(java示例)

1.從view訪問到controller的思路

2.關於office轉換為pdf

實現手段可以參見openoffice轉pdf或者別的軟體,網上有具體的轉換手段,我這裡不多說;

大概程式碼如下,這裡的OfficeToPDFInfo是一個包含輸入輸入路徑等的一個Bean:

/**
	 * 將Office文件轉換為PDF. 執行該函式需要用到OpenOffice, OpenOffice下載地址為
	 * http://www.openoffice.org/
	 * @param sourceFile
	 *            原始檔, 絕對路徑. 可以是Office2003-2007全部格式的文件, Office2010的沒測試. 包括.doc,
	 *            .docx, .xls, .xlsx, .ppt, .pptx等. 示例: F:\\office\\source.doc
	 * @param destFile
	 *            目標檔案. 絕對路徑. 示例: F:\\pdf\\dest.pdf
	 * @return 	    操作成功與否的提示資訊. 如果返回 -1, 表示找不到原始檔, 或url.properties配置錯誤; 如果返回 0,
	 *            則表示操作成功; 返回1, 則表示轉換失敗
	 */
	private static int office2PDF(OfficeToPDFInfo officeToPDFInfo) {
		String sourceFile=officeToPDFInfo.sourceUrl;
		String destFile=officeToPDFInfo.destUrl;
		String OpenOffice_HOME=officeToPDFInfo.openOfficeHOME;
		try {
			File inputFile = new File(sourceFile);
			if (!inputFile.exists()) {
				return -1;// 找不到原始檔, 則返回-1
			}

			// 如果目標路徑不存在, 則新建該路徑
			File outputFile = new File(destFile);
			if (!outputFile.getParentFile().exists()) {
				outputFile.getParentFile().mkdirs();
			}

			//= "D:\\Program Files\\OpenOffice.org 3";//這裡是OpenOffice的安裝目錄, 在我的專案中,為了便於拓展介面,沒有直接寫成這個樣子,但是這樣是絕對沒問題的
			// 如果從檔案中讀取的URL地址最後一個字元不是 '\',則新增'\'
			if (OpenOffice_HOME.charAt(OpenOffice_HOME.length() - 1) != '\\') {
				OpenOffice_HOME += "\\";
			}
			// 啟動OpenOffice的服務
			String command = OpenOffice_HOME
					+ "program\\soffice.exe -headless -accept=\"socket,host=127.0.0.1,port=8100;urp;StarOffice.ServiceManager\" -nofirststartwizard";
			Process pro = Runtime.getRuntime().exec(command);
			// connect to an OpenOffice.org instance running on port 8100
			OpenOfficeConnection connection = new SocketOpenOfficeConnection(
					"127.0.0.1", 8100);
			connection.connect();
			// convert
			DocumentConverter converter = new OpenOfficeDocumentConverter(connection);
			//DocumentConverter converter = new StreamOpenOfficeDocumentConverter(connection); 
			
			converter.convert(inputFile, outputFile);
			// close the connection
			connection.disconnect();
			// 關閉OpenOffice服務的程序
			pro.destroy();
			return 0;
		} catch (FileNotFoundException e) {
			e.printStackTrace();
			return -1;
		} catch (ConnectException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}

		return 1;
	}

public class OfficeToPDFInfo {
	
	/**
	 * 轉換的來源路徑
	 */
	public String sourceUrl; 
	/**
	 * 轉換後的本地路徑
	 */
	public String destUrl;
	/**
	 * 這裡是OpenOffice的安裝目錄
	 */
	public String openOfficeHOME;
	/**
	 * 單個任務執行的超時時間;
	 */
	public Integer task_execution_timeout;
	
}

3.轉換佇列

為了更加合理的利用資源,一個轉換佇列是必不可少的,這裡用一個執行緒list作為執行緒佇列,用另外一個執行緒來管理這個佇列;

用一個map來記錄相關的狀態;

轉換的自定義Properties,其中轉換的並行度最好和其伺服器的cpu虛擬核心數量相同:

public class OfficeToPDFProperty {
	/**
	 * 轉換後的本地儲存資料夾
	 */
	public String destFolder;
	/**
	 * 這裡是OpenOffice的安裝目錄
	 */
	public String openOfficeHome="";
	/**
	 * 單個任務執行的超時時間;
	 */
	public Integer task_execution_timeout=60000;
	/**
	 * 同時轉換的最大併發數量;
	 */
	public int maxThreadCount=0;
	/**
	 * 轉換完成的狀態保持時間
	 */
	public int convertionStatusKeepTime=0;
	/**
	 * 轉換(包括失敗),最多convertionTryCount次;
	 */
	public int convertionTryCount=0;
	/**
	 * 當前pdf轉換狀態的最大儲存數量,超過此數量後將會拋棄時間最久的狀態;
	 */
	public int convertionStatusMaxCount=1000;
	/**
	 * 被轉換的office文件的檔案最大大小,超過此大小後,將不會轉換;
	 * 預設100M,單位位元組;
	 */
	public int fileMaxSize=104857600;
}

轉換的執行緒管理:

public class OfficeToPDFService {
	/**
	 * 將執行前的執行緒加入執行緒池,執行開始後移出執行緒池;執行緒池中儲存的是當前等待轉換的執行緒佇列
	 */
	private static List<Thread> office2PDFThreadPool=new LinkedList<Thread>();
	/**
	 * 當前正在執行的轉換執行緒的數量
	 */
	private static int convertionCount=0; 
	
	private static Thread initConvertionThread=null;
	/**
	 * 初始化執行緒池的執行,每2秒鐘,檢查一次轉換的執行緒池
	 */
	private static synchronized void initConvertionThreadPool(){
		OfficeToPDFProperty officeToPDFProperty=null;
		try {
			officeToPDFProperty = OfficeToPDFPropertyService.getOfficeToPDFProperty();
		} catch (InstantiationException | IllegalAccessException | IOException e1) {
			e1.printStackTrace();
		}
		final int maxCount=officeToPDFProperty.maxThreadCount;
		if(initConvertionThread==null){
			initConvertionThread=new Thread(new Runnable() {	
				@Override
				public void run() {
					while(true){
							int i=0;
							if(getConvertionCount()<maxCount&&getThreadWaitCount()>i){
								Thread converThread=getOffice2PDFThreadByThreadPool(i);
								if(!converThread.isAlive()){
									converThread.start();									
									continue;
								}
								i++;
							}
						try {
							Thread.sleep(2000);
						} catch (InterruptedException e) {
							e.printStackTrace();
						}
					}
				}
			});
			initConvertionThread.start();
		}
	}
	
	/**
	 * 將狀態為非結束狀態的轉換的檔案加入轉換池中
	 * 只轉換OfficeConverionStatus.NoStart的檔案
	 * @param path
	 * @param desPath
	 */
	public static void addOfficeFileToConvertionThreadPool(final String path,final String desPath) {
		initConvertionThreadPool();
		final String originFileName=FileUtils.getFileNameByPath(path);
		OfficeConverionStatus status=MyConstants.getConvertionType(originFileName);
		if(status!=OfficeConverionStatus.NoStart)
			return;
		MyConstants.putConvertionStatus(originFileName, OfficeConverionStatus.Ready);
		final Thread thread=new Thread(new Runnable() {			
			@Override
			public void run() {
				decrementConvertionCount();
				try {		
					incrementConvertionCount();
					MyConstants.putConvertionStatus(originFileName, OfficeConverionStatus.Converting);	
					OfficeToPDFProperty officeToPDFProperty=OfficeToPDFPropertyService.getOfficeToPDFProperty();
					OfficeToPDFInfo info=new OfficeToPDFInfo();
					info.destUrl=desPath;
					info.sourceUrl=path;
					info.openOfficeHOME=officeToPDFProperty.openOfficeHome;
					info.task_execution_timeout=officeToPDFProperty.task_execution_timeout;
					int result=1;
					int convertionTryCount=officeToPDFProperty.convertionTryCount;					
						while(result!=0&&convertionTryCount-->0){
							try {
								result=office2PDF(info);
							} catch (Exception e) {
								e.printStackTrace();
								result=1;
							}							
						}//end while
					if(result==-1){
						MyConstants.putConvertionStatus(originFileName, OfficeConverionStatus.NoFile);						
					}else if(result==1)
					{
						MyConstants.putConvertionStatus(originFileName, OfficeConverionStatus.Faild);
					}else if(result==0){
						MyConstants.putConvertionStatus(originFileName, OfficeConverionStatus.Success);
					}				
				} catch (InstantiationException e) {
					e.printStackTrace();
					MyConstants.putConvertionStatus(originFileName, OfficeConverionStatus.Faild);
				} catch (IllegalAccessException e) {
					e.printStackTrace();
					MyConstants.putConvertionStatus(originFileName, OfficeConverionStatus.Faild);
				} catch (IOException e) {
					e.printStackTrace();
					MyConstants.putConvertionStatus(originFileName,OfficeConverionStatus.Faild);
				}finally{					
					removeThreadFromOffice2PDFThreadPool(Thread.currentThread());
				}							
			}
		});
		addThreadTOOffice2PDFThreadPool(thread);
	}

	
	/**
	 * 得到當前等待的執行緒的數量
	 * @return
	 */
	public static synchronized int getThreadWaitCount(){
		return office2PDFThreadPool.size();
	}
	public static synchronized Thread getOffice2PDFThreadByThreadPool(int index) {
		return office2PDFThreadPool.get(index);
	}

	public static synchronized void addThreadTOOffice2PDFThreadPool(Thread office2pdfThread) {
			office2PDFThreadPool.add(office2pdfThread);		
	}
	
	private static synchronized void removeThreadFromOffice2PDFThreadPool(Thread office2pdfThread) {
		office2PDFThreadPool.remove(office2pdfThread);		
	}

	private synchronized static  int getConvertionCount() {
			return convertionCount;
	}

	private  synchronized static void incrementConvertionCount() {
			convertionCount = convertionCount+1;
	}
	
	private synchronized static void decrementConvertionCount(){
			convertionCount = convertionCount-1;
	}

4.轉換狀態的儲存:

public class MyConstants {
	private static String[] officeFile
		=new String[]{"doc","docx","ppt","pptx","xls","xlsx"};
	public static String[] getOfficeFileExtentionNames(){
		return officeFile;
	}
	/**
	 * 判斷是否是office檔案
	 * @param path
	 * @return
	 */
	public static boolean isOfficeFile(String path){
		if(StringJudgeUtils.isEmpty(path))
			return false;
		for(int i=0;i<officeFile.length;i++){
			String nameString=FileUtils.getFileExtension(path);
			if(nameString.equalsIgnoreCase(officeFile[i])){
				return true;
			}
		}
		return false;
	}
	/**
	*用來儲存轉換的狀態
	**/
	private static Map<String, OfficeConverionStatus> convertionTypes=new HashMap<String, OfficeConverionStatus>();
	/**
	 * 
	 * @param originFileName 轉換之前的檔名稱
	 * @return 當前轉換的狀態
	 */
	public static synchronized OfficeConverionStatus getConvertionType(String originFileName){
		OfficeConverionStatus converionStatus=convertionTypes.get(originFileName);
		if(converionStatus==null)
			converionStatus=OfficeConverionStatus.NoStart;
		return converionStatus;
	}
	/**
	 * 加入新的轉換狀態的時候,會自動移出舊的狀態;
	 * @param originFileName
	 * @param converionStatus 當前轉換的狀態
	 */
	public static synchronized void putConvertionStatus(String originFileName,OfficeConverionStatus converionStatus){
		convertionTypes.put(originFileName,converionStatus);
		putConvertionTime(originFileName, System.currentTimeMillis());
	}
	/**
	 * 將指定檔名的狀態移出
	 * @param originFileName
	 */
	private static synchronized void removeConvertionStatus(String originFileName){
		convertionTypes.remove(originFileName);
	}
	
	/**
	 * 儲存執行緒轉換的結束時間和執行緒轉換的狀態相對應,轉換結束後24小時內沒有訪問結果的將會被清除;
	 */
	private static Map<String, Long> convertionTimeMap=new HashMap<String, Long>();
	/**
	 * 
	 * @param originFileName 轉換之前的檔名稱
	 * @return 當前轉換的狀態
	 */
	public static synchronized Long getConvertionTime(String originFileName){
		Long converionTime=convertionTimeMap.get(originFileName);
		if(converionTime==null)
			converionTime=new Date(2000,1,1).getTime();
		return converionTime;
	}
	
	public static synchronized int getConvertionTypesSize(){
		return convertionTypes.size();
	}
	/**
	 * 加入新的轉換狀態的時候,會自動移出舊的狀態;
	 * 數量超過convertionStatusMaxCount會移出更多老舊狀態
	 * @param originFileName
	 * @param cTime
	 */
	private static synchronized void putConvertionTime(String originFileName,Long cTime){
		convertionTimeMap.put(originFileName,cTime);
		Set<String> keySet=convertionTimeMap.keySet();
		OfficeToPDFProperty officeToPDFProperty;
		try {
			officeToPDFProperty = OfficeToPDFPropertyService.getOfficeToPDFProperty();
			int keepTimeLong=officeToPDFProperty.convertionStatusKeepTime;
			int convertionStatusMaxCount=officeToPDFProperty.convertionStatusMaxCount;
			List<String> removeKeyStrings=new ArrayList<String>();
			do{
				for(String key:keySet){
					Long timeLong=convertionTimeMap.get(key);					
					if(System.currentTimeMillis()-timeLong>keepTimeLong){
						removeKeyStrings.add(key);
					}
				}	
				for(String key:removeKeyStrings){
					convertionTimeMap.remove(key);
					removeConvertionStatus(key);
				}
				keepTimeLong=(int)(keepTimeLong*0.95);
			}while(getConvertionTypesSize()>convertionStatusMaxCount);
			
		} catch (InstantiationException | IllegalAccessException | IOException e) {
			e.printStackTrace();
		}	
	}
	/*public static synchronized void removeConvertionTime(String originFileName){
		convertionTime.remove(originFileName);
	}*/
		
}

5.小結

注意執行緒之間的加鎖互斥,沒有貼出的程式碼無關核心思想,因為時間原因,以後以機會可以做成模組,打包為jar形式,不過openoffice轉pdf的時候,

遇到部分ppt文件可能會出現轉換失敗的情況;