主頁 > 後端開發 > itext7史上最全實戰總結

itext7史上最全實戰總結

2020-10-17 16:15:16 後端開發

1. itext7史上最全實戰總結

1.1. 前言

最近有個需求需要我用Java手動寫一份PDF報告,經過考察幾種pdf開源代碼,最終選取了itext7,此版本為7.1.11,由于發現網上關于該工具的博文比較少,特別是實戰博文幾乎沒有,在我踩完各種坑,最終把PDF成型后,打算把經驗分享出來,本文通過摘錄解釋來說明,內容來自本人GitHub itext-pdf

1.2. 組態檔

專案采用了Spring Cloud config所以配置在git上,僅僅研究itext7不需要用到資料庫等功能,請直接運行PdfMain類的main方法,即可生成模擬的PDF報告

1.3. 版本POM

itext7相關pom

<properties>
    <itext.version>7.1.11</itext.version>
</properties>
<dependencies>
    <!-- itext7 -->
    <dependency>
        <groupId>com.itextpdf</groupId>
        <artifactId>kernel</artifactId>
        <version>${itext.version}</version>
    </dependency>
    <dependency>
        <groupId>com.itextpdf</groupId>
        <artifactId>io</artifactId>
        <version>${itext.version}</version>
    </dependency>
    <dependency>
        <groupId>com.itextpdf</groupId>
        <artifactId>layout</artifactId>
        <version>${itext.version}</version>
    </dependency>
    <dependency>
        <groupId>com.itextpdf</groupId>
        <artifactId>forms</artifactId>
        <version>${itext.version}</version>
    </dependency>
    <dependency>
        <groupId>com.itextpdf</groupId>
        <artifactId>pdfa</artifactId>
        <version>${itext.version}</version>
    </dependency>
    <dependency>
        <groupId>com.itextpdf</groupId>
        <artifactId>pdftest</artifactId>
        <version>${itext.version}</version>
    </dependency>
    <dependency>
        <groupId>com.itextpdf</groupId>
        <artifactId>font-asian</artifactId>
        <version>${itext.version}</version>
    </dependency>
    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-log4j12</artifactId>
        <version>1.7.18</version>
    </dependency>

    <!--itext7 html轉pdf用到的包-->
    <dependency>
        <groupId>com.itextpdf</groupId>
        <artifactId>html2pdf</artifactId>
        <version>3.0.0</version>
    </dependency>
</dependencies>

1.4. 干貨

itext7語意本身和前端css很像,所以有點前端基礎還是比較容易掌握的

1.4.1. 添加圖片

  1. 讀取專案中圖片檔案
  2. 設定邊距
  3. 設定寬高擴大縮小
Image indexImage = new Image(ImageDataFactory.create(GenoReportBuilder.class.getClassLoader().getResource("image/gene.png")));
indexImage.setMargins(-50, -60, -60, -60);
indexImage.scale(1, 1.05f);

1.4.2. 添加指定空白頁

  1. 添加第2頁為空白頁,立即重繪后再繼續添加
pdf.addNewPage(2).flush();

1.4.3. Div、Paragraph

    Div div = new Div();
    div.setWidth(UnitValue.createPercentValue(100));
    div.setHeight(UnitValue.createPercentValue(100));
    div.setHorizontalAlignment(HorizontalAlignment.CENTER);
    Paragraph p1 = new Paragraph();
    p1.setHorizontalAlignment(HorizontalAlignment.CENTER);
    p1.setMaxWidth(UnitValue.createPercentValue(75));
    p1.setMarginTop(180f);
    p1.setCharacterSpacing(0.4f);
    Style large = new Style();
    large.setFontSize(22);
    large.setFontColor(GenoColor.getThemeColor());
    p1.add(new Text("尊敬的 ").addStyle(large));
    ...
    Paragraph p2 = new Paragraph();
    ...
    div.add(p1);
    div.add(p2);
  1. 整塊的內容用Div包裹,這里整塊包裹的好處是什么?一方面排版分明成體系,另一方面若需求是整塊的內容必須在同一個版面,你可以對Div設定div.setKeepTogether(true);,盡量保證若整塊的內容超出了一頁,那這塊內容會自動整塊出現在下一頁,上一頁剩下的就留白了
  2. 可以看到DivParagraph可以設定很多屬性,實際上我們常用的組件除了這兩種,還有TableCellList,他們大部分的屬性都是一樣的,只是部分屬性只在部分組件起效果,所以當你設定某個屬性沒起效果也不用奇怪
  3. Paragraph需要特別注意的一點,想要段落文字居中,不要用setHorizontalAlignment(HorizontalAlignment.CENTER);這是組件的居中對段落無效,甚至對段落里你放Text也無效,需要改用setTextAlignment(TextAlignment.CENTER);
  4. Paragraph段落的行距也是個高頻問題,這里給出官方我看到的解釋,參考https://itextpdf.com/en/resources/books/itext-7-building-blocks/chapter-4-adding-abstractelement-objects-part-1,搜關鍵字setFixedLeading,我的理解該方法設值行高絕對值,官方解釋是兩行文字中間基線之間的距離
  5. 如果想了解詳細的什么屬性哪里能起作用哪里不行,請訪問該地址

UTOOLS1590980957507.png

1.4.4. Table

  1. useAllAvailableWidth表示頁面有多寬,我就有多寬
  2. table.startNewRow();表示新起一行,table每畫一行都要新起一行
  3. 同樣table內容需要居中,和段落一樣,請設定new Cell().setTextAlignment(TextAlignment.CENTER)
  4. 每個table中cell都有默認高度,會比實際輸入字體高些,此時設定setHeight,若更大沒有問題,若高度小于或接近字體大小文字可能就消失了,若想讓Cell高度更接近文字高度,請設定Cellpadding,即cell.setPadding(-2),設定負值即可

1.4.5. Tab,\t

  1. itext7中如果要表示段落前的空格,不能使用\t,但換行可以使用\n

  2. 若要實作Tab效果可以有多個方法

    1. \u00a0符號,大概7、8個該符號可表示tab,可能不是很準確
    p1.add(new Text("\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0壹基因衷心祝愿您身體健康、享受品質生活!"));
    
    1. p1.setFirstLineIndent(24),表示段落前留多少空,需要知道一個字多大,設定成兩倍就行
    2. Tab也是集成AbstractElement的組件,通過以下方式也可實作相同的效果
      p2.add(new Tab());
      p2.addTabStops(new TabStop(20, TabAlignment.LEFT));
    

1.4.6. 換頁

我常用的換頁方法為如下,該方法可保證立即換頁

doc.add(new AreaBreak(AreaBreakType.NEXT_PAGE));

當然PdfDocumentaddNewPage其實也可以用,但有時候你沒把握好重繪時間可能導致某些混亂

1.4.7. 畫圖或畫文字

能畫出多么復雜的圖形看是誰畫了,在我的PDF中,我畫的最復雜的圖形如下

UTOOLS1590982696170.png

該圖形由多個弧形區域加線段加文字組成,包括數字上的小箭頭也是畫出來的,畫這個的代碼過多,想要了解詳細的可以自行下載研究,這里介紹API功能

  1. lineTo畫線段
  2. roundRectangle可用來畫角是弧形的方形,也可以用來畫圓
  3. showText用來畫文字

以上幾種結合填充即可把三角形,多邊形畫出來了

    PdfPage page = pdf.getPage(pdf.getNumberOfPages());
    pageSize = pdf.getDefaultPageSize();
    PdfCanvas pdfCanvas = new PdfCanvas(page);

    pdfCanvas.saveState().moveTo(pageSize.getWidth() / 2 - 100 + i * 40, yOffset - 203)
                    .lineTo(pageSize.getWidth() / 2 - 100 + i * 40, yOffset - 208)
                    .stroke().restoreState();

    pdfCanvas.setLineWidth(2);
        pdfCanvas.setStrokeColor(color);
        pdfCanvas.roundRectangle(pageSize.getWidth() / 2 - 3 + posXOffset, yOffset - 188, 6, 6, 3)
                .stroke();

    pdfCanvas.beginText()
                .setFontAndSize(font, 12)
                .moveText(pageSize.getWidth() / 2 - text.length() * 12 / 2, yOffset - 45);
        pdfCanvas.showText(text);
        pdfCanvas.endText();

1.4.8. Html段落轉Pdf段落

我們可能遇到把一段Html文本轉換成itext7的段落放進來,此時需要用到它的htmlToPdf模塊,該模塊對應POM

    <!--itext7 html轉pdf用到的包-->
    <dependency>
        <groupId>com.itextpdf</groupId>
        <artifactId>html2pdf</artifactId>
        <version>3.0.0</version>
    </dependency>

至于使用,設定好配置屬性,使用也很簡單,通常我們需要支持中文,所有配置如下,字體可以自己換

    ConverterProperties proper = new ConverterProperties();
    //字體設定,解決中文不顯示問題
    FontSet fontSet = new FontSet();
    fontSet.addFont(GenoReportBuilder.class.getClassLoader().getResource("font/SourceHanSansCN-Regular.ttf").getPath(), PdfEncodings.IDENTITY_H);

    FontProvider fontProvider = new FontProvider(fontSet);
    proper.setFontProvider(fontProvider);

    String content = "html內容";
    List<IElement> elements = HtmlConverter.convertToElements(content, proper);

轉換的內容是IElement集合,而IElement是什么呢?給張圖就了解了

UTOOLS1590990939918.png

也就是說只要你的html內容是<div></div>包裹的,你直接把元素轉成itext7的Div然后adddocument就可以實作html內容的添加了,當然你也可以用instanceof判斷不同內容不同處理

如下是我的處理例子供參考,我把輸入html內容樣式進行了一定修改后轉成itext7組件,這里特別提心,html轉過來的itext7組件可能會不支持部分樣式的修改,所以需要在html中進行css樣式的添加,這里我就把字體和高度統一用css設值了

    Div overall = new Div();
    java.util.List<IElement> iElements = getFixContent(value);
    for (IElement iElement : iElements) {
        Style style = new Style();
        style.setFontSize(10);
        style.setCharacterSpacing(0.7f);
        if (iElement instanceof Div) {
            Div div = (Div) iElement;
            java.util.List<IElement> children = div.getChildren();
            // 全部段落改成相同樣式
            this.addParagraphStyleCircle(style, children);
            overall.add(div);
        } else if (iElement instanceof Paragraph) {
            Paragraph element = (Paragraph) iElement;
            overall.add(element.addStyle(style));
        }
    }
    doc.add(overall);
  • getFixContent
    private java.util.List<IElement> getFixContent(String content) {
        if (content.startsWith("<div>")) {
            content = content.replaceAll("<div>", "<div style='line-height:18pt;font-size:16px;'>");
        } else {
            content = "<div style='line-height:18pt;font-size:16px;'>" + content + "</div>";
        }
        return HtmlConverter.convertToElements(content, proper);
    }
  • addParagraphStyleCircle
    private void addParagraphStyleCircle(Style style, java.util.List<IElement> children) {
        for (IElement child : children) {
            if (child instanceof Paragraph) {
                Paragraph element = (Paragraph) child;
                element.addStyle(style);
                java.util.List<IElement> children1 = element.getChildren();
                this.addParagraphStyleCircle(style, children1);
            }
            if (child instanceof Div) {
                Div div = (Div) child;
                java.util.List<IElement> children1 = div.getChildren();
                this.addParagraphStyleCircle(style, children1);
            }
            if (child instanceof Text) {
                Text text = (Text) child;
                text.addStyle(style);
            }
        }
    }

1.4.9. 監聽事件

在撰寫pdf的時候,比如一篇整體的文章,我們需要在頁眉位置添加關于這篇文章的固定文本或者圖形,類似于打個標簽,表示你翻了這么多頁一直在看這篇文章,當第二篇文章的時候就換一個,舉個例子

  • 第一頁

UTOOLS1590992499119.png

  • 第二頁

UTOOLS1590992555997.png

這種需求我們如何實作呢?思路分析發現,我們需要知道什么時候文章內容一頁寫不起了,換了一頁的時候我們需要添加一個同樣的頁眉,這樣我們就需要知道頁是何時添加的,監聽事件就是處理這種問題的

  • pdf是PdfDocument,可添加的事件有START_PAGEINSERT_PAGEREMOVE_PAGEEND_PAGE共四個,如上需求我們需要監聽START_PAGE事件,在事件處理中做相應的處理,我在事件中使用PdfCanvas畫了頭部內容
HeaderTextEvent headerTextEvent = new HeaderTextEvent(title, font);
pdf.addEventHandler(PdfDocumentEvent.START_PAGE, headerTextEvent);
  • HeaderTextEvent類,Painting僅僅是封裝了PdfCanvas
public class HeaderTextEvent implements IEventHandler {

    private String text;
    private PdfFont font;

    public HeaderTextEvent(String text,PdfFont font) {
        this.text = text;
        this.font = font;
    }

    @Override
    public void handleEvent(Event event) {
        PdfDocumentEvent docEvent = (PdfDocumentEvent) event;
        PdfDocument pdfDoc = docEvent.getDocument();
        Painting painting = new Painting(pdfDoc, font);
        painting.drawHeader();
        painting.drawHeaderText(text);
        painting.close();
    }
}

在添加內容前添加相應事件,同時需要記得在不需要的時候移除

// 移除監聽器
pdf.removeEventHandler(PdfDocumentEvent.START_PAGE, headerTextEvent);

1.4.10. 添加目錄

我沒有找到itext7原生是否有目錄添加,根據我自己的需求,我用Table組件來實作了自定義目錄,由于我的PDF是用來列印的,所以我并沒有給目錄添加Link,也就是頁面跳轉,不過當你徹底理解了我的專案,我想這個需求實作也不難

  • 實作效果如下,隨著內容的增長,目錄自動增長

UTOOLS1590993651033.png

先說下遇到的困難,目錄顧明思意,必須要有內容才會有目錄,所以實際上目錄是最后添加的,但如果我們添加內容到最后再跳轉到前面的頁面來添加目錄,有三個問題:

  1. 目錄有幾頁如何知道?
  2. 目錄有幾頁不知道,如何知道內容在第幾頁?
  3. 由于目錄不確定,所以后續內容的頁碼其實也是不確定的,也就是說頁碼也不是一頁頁可以添加過去的

而經過實踐你會發現,我們不能夠回到前幾頁去修改已存在的頁面,因為會提示你已經flush了,不能修改,

這時我看到了movePage這個方法,也就是可以通過移動頁面,把目錄在內容之后生成,后再移動到前幾頁,但是頁碼還是不能修改,發現腦袋不夠想了只能用上屁股,靈光一閃,不能一遍生成為什么不能二次渲染呢?于是研究讀取原pdf在原pdf上修改,二次渲染的時候填上頁碼及移動頁面,主要代碼如下,包括了讀取中間檔案,移動目錄,添加每頁頁碼

PdfReader reader = null;
PdfWriter writer = null;
String inPath = getInPath();
try {
    reader = new PdfReader(new File(inPath));
    writer = new PdfWriter(new File(outPath));
} catch (IOException e) {
    e.printStackTrace();
}
PdfDocument pdf = new PdfDocument(reader, writer);
Document doc = new Document(pdf);
int startPage = 7;
int numberOfPages = pdf.getNumberOfPages();
for (int i = 0; i < catalogSize; i++) {
    pdf.movePage(numberOfPages, startPage);
}
String forbidPage = properties.getProperty("forbidPage");
for (int pageNumber = 1; pageNumber < numberOfPages + 1; pageNumber++) {

    if (pageNumber > 6 + catalogSize && pageNumber != 8 + catalogSize) {
        if (forbidPage != null && (pageNumber - catalogSize) >= Integer.parseInt(forbidPage)) {
            continue;
        }
        PageSize pageSize = pdf.getDefaultPageSize();
        doc.showTextAligned(new Paragraph(String.format("- %d -", pageNumber)), pageSize.getWidth() / 2, 30, pageNumber, TextAlignment.CENTER, VerticalAlignment.MIDDLE, 0);
    }
}

1.5. 總結

經過上述總結,我基本上把專案中的大多基本點和難點都概括進去了,初次用itext7寫PDF的同學基本會遇到的問題基本都在上述這些,不理解的就把專案下下來運行Main方法慢慢除錯,理解透我這個專案,還有其它問題那基本只能翻官網了

專案Github: https://github.com/tzxylao/onegeno-itext-pdf
itext7官網:https://itextpdf.com/

轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/176250.html

標籤:Java

上一篇:學習筆記之網路編程

下一篇:溫故知新-多執行緒-深入刨析synchronized

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 【C++】Microsoft C++、C 和匯編程式檔案

    ......

    uj5u.com 2020-09-10 00:57:23 more
  • 例外宣告

    相比于斷言適用于排除邏輯上不可能存在的狀態,例外通常是用于邏輯上可能發生的錯誤。 例外宣告 Item 1:當函式不可能拋出例外或不能接受拋出例外時,使用noexcept 理由 如果不打算拋出例外的話,程式就會認為無法處理這種錯誤,并且應當盡早終止,如此可以有效地阻止例外的傳播與擴散。 示例 //不可 ......

    uj5u.com 2020-09-10 00:57:27 more
  • Codeforces 1400E Clear the Multiset(貪心 + 分治)

    鏈接:https://codeforces.com/problemset/problem/1400/E 來源:Codeforces 思路:給你一個陣列,現在你可以進行兩種操作,操作1:將一段沒有 0 的區間進行減一的操作,操作2:將 i 位置上的元素歸零。最終問:將這個陣列的全部元素歸零后操作的最少 ......

    uj5u.com 2020-09-10 00:57:30 more
  • UVA11610 【Reverse Prime】

    本人看到此題沒有翻譯,就附帶了一個自己的翻譯版本 思考 這一題,它的第一個要求是找出所有 $7$ 位反向質數及其質因數的個數。 我們應該需要質數篩篩選1~$10^{7}$的所有數,這里就不慢慢介紹了。但是,重讀題,我們突然發現反向質數都是 $7$ 位,而將它反過來后的數字卻是 $6$ 位數,這就說明 ......

    uj5u.com 2020-09-10 00:57:36 more
  • 統計區間素數數量

    1 #pragma GCC optimize(2) 2 #include <bits/stdc++.h> 3 using namespace std; 4 bool isprime[1000000010]; 5 vector<int> prime; 6 inline int getlist(int ......

    uj5u.com 2020-09-10 00:57:47 more
  • C/C++編程筆記:C++中的 const 變數詳解,教你正確認識const用法

    1、C中的const 1、區域const變數存放在堆疊區中,會分配記憶體(也就是說可以通過地址間接修改變數的值)。測驗代碼如下: 運行結果: 2、全域const變數存放在只讀資料段(不能通過地址修改,會發生寫入錯誤), 默認為外部聯編,可以給其他源檔案使用(需要用extern關鍵字修飾) 運行結果: ......

    uj5u.com 2020-09-10 00:58:04 more
  • 【C++犯錯記錄】VS2019 MFC添加資源不懂如何修改資源宏ID

    1. 首先在資源視圖中,添加資源 2. 點擊新添加的資源,復制自動生成的ID 3. 在解決方案資源管理器中找到Resource.h檔案,編輯,使用整個專案搜索和替換的方式快速替換 宏宣告 4. Ctrl+Shift+F 全域搜索,點擊查找全部,然后逐個替換 5. 為什么使用搜索替換而不使用屬性視窗直 ......

    uj5u.com 2020-09-10 00:59:11 more
  • 【C++犯錯記錄】VS2019 MFC不懂的批量添加資源

    1. 打開資源頭檔案Resource.h,在其中預先定義好宏 ID(不清楚其實ID值應該設定多少,可以先新建一個相同的資源項,再在這個資源的ID值的基礎上遞增即可) 2. 在資源視圖中選中專案資源,按F7編輯資源檔案,按 ID 型別 相對路徑的形式添加 資源。(別忘了先把檔案拷貝到專案中的res檔案 ......

    uj5u.com 2020-09-10 01:00:19 more
  • C/C++編程筆記:關于C++的參考型別,專供新手入門使用

    今天要講的是C++中我最喜歡的一個用法——參考,也叫別名。 參考就是給一個變數名取一個變數名,方便我們間接地使用這個變數。我們可以給一個變數創建N個參考,這N + 1個變數共享了同一塊記憶體區域。(參考型別的變數會占用記憶體空間,占用的記憶體空間的大小和指標型別的大小是相同的。雖然參考是一個物件的別名,但 ......

    uj5u.com 2020-09-10 01:00:22 more
  • 【C/C++編程筆記】從頭開始學習C ++:初學者完整指南

    眾所周知,C ++的學習曲線陡峭,但是花時間學習這種語言將為您的職業帶來奇跡,并使您與其他開發人員區分開。您會更輕松地學習新語言,形成真正的解決問題的技能,并在編程的基礎上打下堅實的基礎。 C ++將幫助您養成良好的編程習慣(即清晰一致的編碼風格,在撰寫代碼時注釋代碼,并限制類內部的可見性),并且由 ......

    uj5u.com 2020-09-10 01:00:41 more
最新发布
  • Rust中的智能指標:Box<T> Rc<T> Arc<T> Cell<T> RefCell<T> Weak

    Rust中的智能指標是什么 智能指標(smart pointers)是一類資料結構,是擁有資料所有權和額外功能的指標。是指標的進一步發展 指標(pointer)是一個包含記憶體地址的變數的通用概念。這個地址參考,或 ” 指向”(points at)一些其 他資料 。參考以 & 符號為標志并借用了他們所 ......

    uj5u.com 2023-04-20 07:24:10 more
  • Java的值傳遞和參考傳遞

    值傳遞不會改變本身,參考傳遞(如果傳遞的值需要實體化到堆里)如果發生修改了會改變本身。 1.基本資料型別都是值傳遞 package com.example.basic; public class Test { public static void main(String[] args) { int ......

    uj5u.com 2023-04-20 07:24:04 more
  • [2]SpinalHDL教程——Scala簡單入門

    第一個 Scala 程式 shell里面輸入 $ scala scala> 1 + 1 res0: Int = 2 scala> println("Hello World!") Hello World! 檔案形式 object HelloWorld { /* 這是我的第一個 Scala 程式 * 以 ......

    uj5u.com 2023-04-20 07:23:58 more
  • 理解函式指標和回呼函式

    理解 函式指標 指向函式的指標。比如: 理解函式指標的偽代碼 void (*p)(int type, char *data); // 定義一個函式指標p void func(int type, char *data); // 宣告一個函式func p = func; // 將指標p指向函式func ......

    uj5u.com 2023-04-20 07:23:52 more
  • Django筆記二十五之資料庫函式之日期函式

    本文首發于公眾號:Hunter后端 原文鏈接:Django筆記二十五之資料庫函式之日期函式 日期函式主要介紹兩個大類,Extract() 和 Trunc() Extract() 函式作用是提取日期,比如我們可以提取一個日期欄位的年份,月份,日等資料 Trunc() 的作用則是截取,比如 2022-0 ......

    uj5u.com 2023-04-20 07:23:45 more
  • 一天吃透JVM面試八股文

    什么是JVM? JVM,全稱Java Virtual Machine(Java虛擬機),是通過在實際的計算機上仿真模擬各種計算機功能來實作的。由一套位元組碼指令集、一組暫存器、一個堆疊、一個垃圾回收堆和一個存盤方法域等組成。JVM屏蔽了與作業系統平臺相關的資訊,使得Java程式只需要生成在Java虛擬機 ......

    uj5u.com 2023-04-20 07:23:31 more
  • 使用Java接入小程式訂閱訊息!

    更新完微信服務號的模板訊息之后,我又趕緊把微信小程式的訂閱訊息給實作了!之前我一直以為微信小程式也是要企業才能申請,沒想到小程式個人就能申請。 訊息推送平臺🔥推送下發【郵件】【短信】【微信服務號】【微信小程式】【企業微信】【釘釘】等訊息型別。 https://gitee.com/zhongfuch ......

    uj5u.com 2023-04-20 07:22:59 more
  • java -- 緩沖流、轉換流、序列化流

    緩沖流 緩沖流, 也叫高效流, 按照資料型別分類: 位元組緩沖流:BufferedInputStream,BufferedOutputStream 字符緩沖流:BufferedReader,BufferedWriter 緩沖流的基本原理,是在創建流物件時,會創建一個內置的默認大小的緩沖區陣列,通過緩沖 ......

    uj5u.com 2023-04-20 07:22:49 more
  • Java-SpringBoot-Range請求頭設定實作視頻分段傳輸

    老實說,人太懶了,現在基本都不喜歡寫筆記了,但是網上有關Range請求頭的文章都太水了 下面是抄的一段StackOverflow的代碼...自己大修改過的,寫的注釋挺全的,應該直接看得懂,就不解釋了 寫的不好...只是希望能給視頻網站開發的新手一點點幫助吧. 業務場景:視頻分段傳輸、視頻多段傳輸(理 ......

    uj5u.com 2023-04-20 07:22:42 more
  • Windows 10開發教程_編程入門自學教程_菜鳥教程-免費教程分享

    教程簡介 Windows 10開發入門教程 - 從簡單的步驟了解Windows 10開發,從基本到高級概念,包括簡介,UWP,第一個應用程式,商店,XAML控制元件,資料系結,XAML性能,自適應設計,自適應UI,自適應代碼,檔案管理,SQLite資料庫,應用程式到應用程式通信,應用程式本地化,應用程式 ......

    uj5u.com 2023-04-20 07:22:35 more