一、文件下載
項目文件存入ambry文件服務器,並返回可訪問url,利用url下載該文件。
public void downloadByURL(@Param("download")String download,@Param("description")String description,
HttpServletRequest request, HttpServletResponse response){
//這裏description爲文件名,用以下載的時候顯示。
if(!description.endsWith(".zip")){
description = description+".zip";
}
//對response頭進行設置
response.reset();
response.setContentType("application/octet-stream");
response.setCharacterEncoding("UTF-8");
//設置文件下載是以附件的形式下載
response.setHeader("Content-Disposition", String.format("attachment; filename=\"%s\"", description));
try {
//根據文件url鏈接,獲取文件字節數組
byte[] buffer = HTTPDownload.downLoadFromUrl(download);
//從response頭獲取輸出流,並向裏面寫
OutputStream out = response.getOutputStream();
//向流裏面寫,此時瀏覽器便能監聽到
out.write(buffer);
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
import java.net.HttpURLConnection;
import java.net.URL;
public static byte[] downLoadFromUrl(String urlStr) throws IOException {
URL url = new URL(urlStr);
HttpURLConnection conn = (HttpURLConnection)url.openConnection();
//設置超時間爲5秒
conn.setConnectTimeout(5*1000);
//得到文件的輸入流
InputStream inputStream = conn.getInputStream();
//將輸入流轉爲字節數組
byte[] buffer = readInputStream(inputStream);
return buffer;
}
/**
* 從輸入流中獲取字節數組
* @param inputStream
* @return
* @throws IOException
*/
public static byte[] readInputStream(InputStream inputStream) throws IOException {
byte[] buffer = new byte[4096];
int len = 0;
//造個輸出流,用來輸出字節數組
ByteArrayOutputStream bos = new ByteArrayOutputStream();
while((len = inputStream.read(buffer)) != -1) {
bos.write(buffer, 0, len);
}
bos.close();
inputStream.close();
return bos.toByteArray();
}
Content-disposition中Attachment和inline的區別
java web中下載文件時,我們一般設置 Content-Disposition 告訴瀏覽器下載文件的名稱,是否在瀏覽器中內嵌顯示.
- Content-disposition: inline; filename=foobar.pdf
- 表示瀏覽器內嵌顯示一個文件
- Content-disposition: attachment; filename=foobar.pdf
- 表示會下載文件,如火狐瀏覽器中
但是我們使用的chrome,通常設置了默認下載地址,所以不會提示。如果沒有設置,則會提示選擇下載路徑。
看如下代碼 spring mvc中:
@ResponseBody
@RequestMapping(value = "/download",produces="application/octet-stream")
public byte[] downloadFile(HttpServletRequest request, HttpServletResponse response,String contentType2)
throws IOException {
byte[]bytes=FileUtils.getBytes4File("D:\\Temp\\cc.jpg");
response.addHeader("Content-Disposition", "inline;filename=\"a.jpg\"");
return bytes;
}
如上代碼中是內嵌顯示圖片呢?還是會彈框下載呢?
答案是:彈框下載
爲什麼呢?設置爲inline應該是內嵌顯示啊!
因爲response content type設置成了”application/octet-stream”
注意:我們說是內嵌顯示還是下載,那 一定是針對可內嵌顯示的類型 ,例如”image/jpeg”,”image/png”等.
看下面的例子:設置response content type爲” image/jpeg “
@ResponseBody
@RequestMapping(value = "/download",produces="image/jpeg")
public byte[] downloadFile(HttpServletRequest request, HttpServletResponse response,String contentType2,String downloadType)
throws IOException {
byte[]bytes=FileUtils.getBytes4File("D:\\Temp\\cc.jpg");
response.addHeader("Content-Disposition", downloadType+";filename=\"a.jpg\"");
return bytes;
}
對於Content-type對照表可參照http://tool.oschina.net/commons
在瀏覽器中訪問:http://localhost:8080/download?downloadType=inline 時就內嵌顯示:
當在瀏覽器中訪問:http://localhost:8080/download?downloadType=attachment 時就彈框下載.
二、文件上傳
放一段hash大神的ambry文件上傳。
public class HTTPUpload {
private static final Logger log = LoggerFactory.getLogger(HTTPUpload.class);
private final String ambryUrl;
private final String tmpFilePath;
public HTTPUpload(final String ambryUrl) {
this.ambryUrl = ambryUrl;
//筆記一,筆記二1
this.tmpFilePath = StrUtils.makeString(HTTPUpload.class.getResource("").getFile().toString(), "tmp");
File file1 = new File(tmpFilePath);
//文件不存在則創建
if(!file1.exists()){
file1.mkdirs();
//設置文件權限
file1.setExecutable(true,false);
file1.setReadable(true,false);
file1.setWritable(true,false);
}
Scheduler scheduler = new Scheduler();
scheduler.schedule("50 13 * * *", new DeleteTask(tmpFilePath));
}
public String uploadFile(MultipartFile file1, long size, String ServiceId, String owner, String fileFormat,
String description) {
HttpURLConnection connection = null;
OutputStream os = null;
DataInputStream is = null;
InputStream inputStream = null;
String message = "empty";
int count = 0;
while (count <= 3) {
try {
//筆記二2
File file = new File(StrUtils.makeString(tmpFilePath, "/", file1.getOriginalFilename()));
//筆記二3
file1.transferTo(file);
//筆記三
if (!fileFormat.equals("image")) {
inputStream = new StreamGenerator(file, tmpFilePath).getInputStream();
} else {
inputStream = new FileInputStream(file);
}
//筆記四
int length = inputStream.available();
//筆記五
connection = (HttpURLConnection) new URL(ambryUrl).openConnection();
connection.setRequestMethod("POST");
connection.setRequestProperty("Content-Type", "application/octet-stream");
//設置是否從httpUrlConnection讀入,默認情況下是true;
connection.setDoOutput(true);
//設置是否向httpUrlConnection輸出,默認爲false
connection.setDoInput(true);
connection.setRequestProperty("x-ambry-blob-size", length + "");
connection.addRequestProperty("x-ambry-service-id", ServiceId);
connection.addRequestProperty("x-ambry-owner-id", owner);
connection.addRequestProperty("x-ambry-content-type", fileFormat);
connection.addRequestProperty("x-ambry-um-description", description);
connection.connect();
//筆記六
os = new BufferedOutputStream(connection.getOutputStream());
byte[] buffer = new byte[4096];
int bytes_read;
//只要可以讀取到數據,就輸出寫到buffer中
while ((bytes_read = inputStream.read(buffer)) != -1) {
os.write(buffer, 0, bytes_read);
}
//數據讀取完關閉inputStream
os.close();
inputStream.close();
//筆記七
String location = connection.getHeaderField("Location");
return StrUtils.makeString(ambryUrl, location);
} catch (Throwable e) {
//筆記八
count++;
try {
TimeUnit.SECONDS.sleep(1L);
} catch (InterruptedException e1) {
}
log.warn("file-upload-failed:" + JSON.toJSONString(file1) + "|||" + "{ServiceId:" + ServiceId + ",owner:" + owner + ",fileFormat:" + fileFormat + ",description:" + description + "}");
log.warn(ExceptionUtils.getStackTrace(e));
} finally {
try {
if (os != null) {
os.close();
}
} catch (IOException e) {
log.warn(ExceptionUtils.getStackTrace(e));
}
try {
if (is != null) {
is.close();
}
} catch (IOException e) {
log.warn(ExceptionUtils.getStackTrace(e));
}
if (connection != null) {
connection.disconnect();
}
}
}
return null;
}
}
筆記一:
Class.getResource()與Class.getResourceAsStream()方法,但很多人還是不太懂它的用法,因爲很多人(比如不久前的我)都不知道應該傳怎麼樣的參數給它,當然,有些人己經用得如火純青,這些人是不需要照顧的,在此僅給不會或者還不是很熟的人解釋一點點。
比如我們有以下目錄
|–project
|--src
|--javaapplication
|--Test.java
|--file1.txt
|--file2.txt
|--build
|--javaapplication
|--Test.class
|--file3.txt
|--file4.txt
在上面的目錄中,有一個src目錄,這是JAVA源文件的目錄,有一個build目錄,這是JAVA編譯後文件(.class文件等)的存放目錄
那麼,我們在Test類中應該如何分別獲得
file1.txt file2.txt file3.txt file4.txt這四個文件呢?
首先講file3.txt與file4.txt
file3.txt:
方法一:File file3 = new File(Test.class.getResource(“file3.txt”).getFile());
方法二:File file3 = new File(Test.class.getResource(“/javaapplication/file3.txt”).getFile());
方法三:File file3 = new File(Test.class.getClassLoader().getResource(“javaapplication/file3.txt”).getFile());
file4.txt:
方法一:File file4 = new File(Test.class.getResource(“/file4.txt”).getFile());
方法二:File file4 = new File(Test.class.getClassLoader().getResource(“file4.txt”).getFile());
很好,我們可以有多種方法選擇,但是file1與file2文件呢?如何獲得?
答案是,你只能寫上它們的絕對路徑,不能像file3與file4一樣用class.getResource()這種方法獲得,它們的獲取方法如下
假如整個project目錄放在c:/下,那麼file1與file2的獲取方法分別爲
file1.txt
方法一:File file1 = new File(“c:/project/src/javaapplication/file1.txt”);
方法二:。。。沒有
file2.txt
方法一:File file2 = new File(“c:/project/src/file2.txt”);
方法二:。。。也沒有
總結一下,就是你想獲得文件,你得從最終生成的.class文件爲着手點,不要以.java文件的路徑爲出發點,因爲真正使用的就是.class,不會拿個.java文件就使用,因爲java是編譯型語言嘛
筆記二:
在初始化HTTPUpload對象的時候,會在該類目錄下創建個臨時目錄,D:/projectSource/*/target/classes/com/*/ambry/tmp,見筆記二1。
在使用上傳方法時,在前面的目錄下根據文件名new一個File,但file對象內容爲空,見筆記二2。
將傳過來的文件寫入file對象中,見筆記二3。
筆記三:
如果文件不是image類型,則將文件壓縮爲zip之後得到輸入流。如果是圖片,則直接得到圖片文件的輸入流。
調用如下方法,返回zip文件的輸入流
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
public class StreamGenerator {
private static File file;
private static String tempFilePath;
public StreamGenerator(File file,String tempFilePath){
this.file = file;
this.tempFilePath = tempFilePath;
}
/**
* 壓縮傳入的文件,返回流
* @return inputstream
*/
public static InputStream getInputStream() {
String zipName = IDGenerator.getID(Constants.PUBLIC_COMPONENT_SYSTEM);
//判斷是否有文件夾,如果沒有就創建
File tempF = new File(tempFilePath);
if(!tempF.exists()){
tempF.mkdirs();
}
/**
* 壓縮並在指定目錄生成臨時zip文件
*/
byte[] buffer = new byte[4096];
ZipOutputStream out = null;
try {
//在臨時目錄下根據前面生成的zip包名,new一個fileoutput(zipoutput)流
out = new ZipOutputStream(new FileOutputStream(tempFilePath + zipName + ".zip"));
FileInputStream fileInput = new FileInputStream(file);
//向流中加入zip實體(此時爲空,名爲文件名)
out.putNextEntry(new ZipEntry(file.getName()));
int temp = 0;
//讀取文件並打包
while ((temp = fileInput.read(buffer)) > 0) {
out.write(buffer, 0, temp);
}
out.closeEntry();
fileInput.close();
out.close();
} catch(Exception ex){
ex.printStackTrace();
}
/**
* 把壓縮包讀取爲輸出流
*/
//得到這個有內容的zip對象
File filezip = new File(tempFilePath + zipName + ".zip");
ByteArrayOutputStream byteOut = null;
try {
byteOut = new ByteArrayOutputStream();
FileInputStream FileIn = new FileInputStream(filezip);
BufferedInputStream bufferInput = new BufferedInputStream(FileIn);
int k = bufferInput.read();
while (k != -1) {
byteOut.write(k);
k = bufferInput.read();
}
bufferInput.close();
FileIn.close();
} catch(Exception ex) {
ex.printStackTrace();
}
/**
* 輸出流轉爲uploadFile方法的輸入流
*/
ByteArrayInputStream inStream = new ByteArrayInputStream(byteOut.toByteArray());
return inStream;
}
}
此處爲他人所寫。根據輸入流得到zip的輸出流,得到字節數組,然後再回到輸入流,感覺多此一舉,也許可能存在流轉換的問題,以後有時間去驗證。
筆記四:
獲得文件的可用大小,此處爲ambry用,如果傳錯會報錯。
筆記五:
向ambry發送http請求(post)
筆記六:
connection.getOutputStream()通過http連接,獲取輸出流。幷包裝成輸出字節流,將zip或圖片獲取的輸入流,寫入此輸出流。
筆記七:
location頭裏面的東西,就是文件存儲後的唯一標識。
筆記八:
三次嘗試機制,每次失敗延遲1毫秒:TimeUnit.SECONDS.sleep(1L)。