0 問題的提出
項目中使用到了傳真(Fax)功能,而傳真收到的文件一般都是tif格式的。
我們需要把這樣的tif文件展示在前端。
後端會從某處下載這個tif文件,可以用字節數組表示這個文件。
1. 思路
思路1:修改擴展名。把tif修改爲jpg,png之類的擴展名
失敗,修改後直接用Chrome都無法正常展示。顯示一個小方格,即便是放大很多,也還是顯示不出圖片。
思路2:講byte[]返回Base64編碼字符串。
我們知道:<img>標籤的src可以使用Base64編碼字符串來顯示圖片。那麼我們直接返回tif的Base64字符串,然後指定其類型爲image/jpg會怎樣呢?
失敗,無論修改爲image/jpg還是image/tig等其它類型,其顯示結果都和直接修改擴展名在chrome下的顯示結果一樣。
<img src="data:image/jpg;base64,XXXXXXBase64StringXXXXX" />
思路3:用Java把tif轉換爲jpg
在調查中發現,Java有JAI可以實現將tif轉換爲jpg格式。代碼比較簡單:
import java.io.FileOutputStream;
import java.io.OutputStream;
import javax.media.jai.JAI;
import javax.media.jai.RenderedOp;
import com.sun.media.jai.codec.ImageCodec;
import com.sun.media.jai.codec.ImageEncoder;
import com.sun.media.jai.codec.JPEGEncodeParam;
public class Test {
public static void main(String[] args) throws Exception {
/* tif轉換到jpg格式 */
String input = "D:/1.tif";
String output = "D:/1.jpg";
RenderedOp src = JAI.create("fileload", input);
OutputStream os = new FileOutputStream(output);
// ImageEncodeParam接口有JPEGEncodeParam,BMPXXX,PNGXXX,TIFFXXX實現類
JPEGEncodeParam param = new JPEGEncodeParam();
// 指定格式類型,jpg 屬於 JPEG 類型
ImageEncoder enc = ImageCodec.createImageEncoder("JPEG", os, param);
enc.encode(src);
os.close();
}
}
放棄,並不是這個方法不可用,而是JAI的包在Maven倉庫中找不到。
項目中使用的是客戶方私服,想要加個jar進去比較困難。
思路4:Java把tif轉換爲pdf
項目中已經使用了iText 2.1.7,那麼把tif直接作爲圖片放在pdf文件中是不是就可以了呢?
package com.example.async;
import java.io.*;
import com.lowagie.text.*;
import com.lowagie.text.pdf.*;
import com.lowagie.text.pdf.codec.*;
public class TiffToPDF {
public static void main(String[] args) {
if (args.length < 1) {
System.out.println("Usage: Tiff2Pdf file1.tif [file2.tif fileN.tif]");
System.exit(1);
}
String tiff, pdf;
for (int i = 0; i < args.length; i++) {
tiff = args[i];
pdf = tiff.substring(0, tiff.lastIndexOf('.') + 1) + "pdf";
Document document = new Document(PageSize.LETTER, 0, 0, 0, 0);
int pages = 0, comps = 0;
try {
PdfWriter writer = PdfWriter.getInstance(document, new FileOutputStream(pdf));
document.open();
PdfContentByte cb = writer.getDirectContent();
RandomAccessFileOrArray ra = null;
try {
ra = new RandomAccessFileOrArray(tiff);
comps = TiffImage.getNumberOfPages(ra);
} catch (Throwable e) {
System.out.println("Exception in " + tiff + " " + e.getMessage());
continue;
}
System.out.println("Processing: " + tiff);
for (int c = 0; c < comps; ++c) {
try {
Image img = TiffImage.getTiffImage(ra, c + 1);
if (img != null) {
System.out.println("page " + (c + 1));
img.scalePercent(7200f / img.getDpiX(), 7200f / img.getDpiY());
document.setPageSize(new Rectangle(img.getScaledWidth(), img.getScaledHeight()));
img.setAbsolutePosition(0, 0);
cb.addImage(img);
document.newPage();
++pages;
}
} catch (Throwable e) {
System.out.println("Exception " + tiff + " page " + (c + 1) + " " + e.getMessage());
}
}
ra.close();
document.close();
} catch (Throwable e) {
e.printStackTrace();
}
System.out.println("done...");
}
}
}
上面的代碼存在兩個問題:
1. 一直沒法調整tif圖片在pdf文件的大小,無法讓圖片填滿整個pdf。預期至少是水平方面填滿,垂直方向按比例拉伸。
2. 部分tif處理會報異常:java.lang.RuntimeException: Scanline must begin with EOL code word. 撒(主要是說tif文件可能是invalid或者corrupt了,可以通過指定recoverFromImageError參數來讀取無效的tiff,但是似乎2.1.7版本並沒有這個參數)。
思路5:使用JavaScript:tiff.js來顯示tif
在後續的調查中發現,有個tiff.js可以實現對tiff圖片的預覽展示。
注意:除了tiff.js,其在GitHub上還說了兩個其它方案:
https://github.com/photopea/UTIF.js
https://github.com/image-js/tiff
tiff.js用法非常簡單,在引入tiff.min.js後,使用如下代碼即可顯示tif:
// 1.發送請求獲取arraybuffer
// 2.使用Tiff轉換爲canvas並顯示
var xhr = new XMLHttpRequest();
xhr.responseType = 'arraybuffer';
xhr.open('GET', "url/of/a/tiff/image/file.tiff");
xhr.onload = function (e) {
var tiff = new Tiff({buffer: xhr.response});
var canvas = tiff.toCanvas();
document.body.append(canvas);
};
xhr.send();
上面使用原生的XMLHttpRequest去獲取tif文件的arraybuffer,我們項目中使用的是接口返回的byte[](Jackson返回的時候會對byte[]轉換爲Base64返回),後端代碼如下所示:
@Controller
@RequestMapping("/tif")
public class TifController {
@GetMapping("/download")
@ResponseBody
public Map<String, Object> downloadTif() {
String fileName = "1.tif";
String filePath = "D:" + File.separator + fileName;
File file = new File(filePath);
Map<String, Object> result = new HashMap<String, Object>();
result.put("fileName", fileName);
try {
// Jackson會對byte[]會編碼爲Base64字符串,所以前端需要解碼
result.put("fileContent", FileUtils.readFileToByteArray(file));
} catch (IOException e) {
// TODO exception handle
}
result.put("filelLength", file.length());
return result;
}
}
前端AngularJS展示的代碼調整如下:
$scope.showTif = function() {
$http({
method: 'GET',
responseType: 'json',
url: '/tif/download'
}).then(function successCallback(response) {
var buffer = $scope.decodeBase64(response.data.fileContent);
var tiff = new Tiff({
buffer : buffer
});
var canvas = tiff.toCanvas();
var width = tiff.width();
var height = tiff.height();
if (canvas) {
$('#output').empty().append(canvas);
}
}, function errorCallback(response) {
console.log("error...");
});
}
$scope.decodeBase64 = function(base64Str) {
var bString = atob(base64Str);
var len = bString.length;
var arr = new Uint8Array(len);
while (len--) {
arr[len] = bString.charCodeAt(len);
}
return arr;
}
詳細代碼見附件,示例訪問地址:
http://localhost:8080/tiff-demo-upload-tiff.html
http://localhost:8080/angularjs-show-tif.html
源代碼:https://download.csdn.net/download/u012383839/11830615
參考
1. [前端img標籤顯示 base64格式的圖片](https://blog.csdn.net/kukudehui/article/details/80409522)
2. [JAVA 實現jpg/tif/bmp 等圖片之間格式得互相轉換](https://www.iteye.com/blog/yangpanwww-1127787)
3. [java實現 tiff圖片 轉 JPG圖片(完美解決)](https://blog.csdn.net/mufeng633/article/details/83380510)
4. [java使用itextpdf將圖片轉換成pdf時DPI的問題](https://blog.csdn.net/lizhengyu891231/article/details/81052594)
5. [Tiff to PDF in Java (itext)](https://blog.csdn.net/zzh87615/article/details/5839790)
6. [Exception: Scanline must begin with EOL code word](https://stackoverflow.com/questions/29787388/exception-when-converting-tiff-file-to-pdf-file-with-itext)
7. [Github tiff.js] (https://github.com/seikichi/tiff.js)
8. [GitHub:使用tiff.js上傳tif並展示的例子](http://seikichi.github.io/tiff.js/upload.html)
9. [Spring Boot直接訪問靜態html頁面](https://blog.csdn.net/csdn_liumh/article/details/81939664)