java基礎知識--IO篇

一、文件下載

  項目文件存入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)。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章