easypoi結(jié)合spring-boot 快速使用

我們不造輪子,只是輪子的搬運工。(其實最好是造輪子摊册,造比別人好的輪子)

開發(fā)中經(jīng)常會遇到excel的處理,導(dǎo)入導(dǎo)出解析等等颊艳,java中比較流行的用poi茅特,但是每次都要寫大段工具類來搞定這事兒,此處推薦一個別人造好的輪子【easypoi】棋枕,下面介紹下“輪子”的使用白修。

1、 在pom.xml中加入依賴

<!--excel操作-->
<dependency>
    <groupId>cn.afterturn</groupId>
    <artifactId>easypoi-spring-boot-starter</artifactId>
    <version>3.3.0</version>
</dependency>

或者引入下面的依賴:

<dependency>
    <groupId>cn.afterturn</groupId>
    <artifactId>easypoi-base</artifactId>
    <version>3.0.3</version>
</dependency>
<dependency>
    <groupId>cn.afterturn</groupId>
    <artifactId>easypoi-web</artifactId>
    <version>3.0.3</version>
</dependency>
<dependency>
    <groupId>cn.afterturn</groupId>
    <artifactId>easypoi-annotation</artifactId>
    <version>3.0.3</version>
</dependency>

2重斑、編寫實體類

此處注意必須要有空構(gòu)造函數(shù)兵睛,否則會報錯“對象創(chuàng)建錯誤”
關(guān)于注解@Excel,其他還有@ExcelCollection窥浪,@ExcelEntity 祖很,@ExcelIgnore@ExcelTarget等漾脂,此處我們用不到突琳,可以去官方查看更多

屬性 類型 類型 說明
name String null 列名
needMerge boolean fasle 縱向合并單元格
orderNum String "0" 列的排序,支持name_id
replace String[] {} 值得替換 導(dǎo)出是{a_id,b_id} 導(dǎo)入反過來
savePath String "upload" 導(dǎo)入文件保存路徑
type int 1 導(dǎo)出類型 1 是文本 2 是圖片,3 是函數(shù),10 是數(shù)字 默認是文本
width double 10 列寬
height double 10 列高,后期打算統(tǒng)一使用@ExcelTarget的height,這個會被廢棄,注意
isStatistics boolean fasle 自動統(tǒng)計數(shù)據(jù),在追加一行統(tǒng)計,把所有數(shù)據(jù)都和輸出這個處理會吞沒異常,請注意這一點
isHyperlink boolean false 超鏈接,如果是需要實現(xiàn)接口返回對象
isImportField boolean true 校驗字段,看看這個字段是不是導(dǎo)入的Excel中有,如果沒有說明是錯誤的Excel,讀取失敗,支持name_id
exportFormat String "" 導(dǎo)出的時間格式,以這個是否為空來判斷是否需要格式化日期
importFormat String "" 導(dǎo)入的時間格式,以這個是否為空來判斷是否需要格式化日期
format String "" 時間格式,相當(dāng)于同時設(shè)置了exportFormat 和 importFormat
databaseFormat String "yyyyMMddHHmmss" 導(dǎo)出時間設(shè)置,如果字段是Date類型則不需要設(shè)置 數(shù)據(jù)庫如果是string類型,這個需要設(shè)置這個數(shù)據(jù)庫格式,用以轉(zhuǎn)換時間格式輸出
numFormat String "" 數(shù)字格式化,參數(shù)是Pattern,使用的對象是DecimalFormat
imageType int 1 導(dǎo)出類型 1 從file讀取 2 是從數(shù)據(jù)庫中讀取 默認是文件 同樣導(dǎo)入也是一樣的
suffix String "" 文字后綴,如% 90 變成90%
isWrap boolean true 是否換行 即支持\n
mergeRely int[] {} 合并單元格依賴關(guān)系,比如第二列合并是基于第一列 則{1}就可以了
mergeVertical boolean fasle 縱向合并內(nèi)容相同的單元格
import cn.afterturn.easypoi.excel.annotation.Excel;
import lombok.Data;
import lombok.EqualsAndHashCode;

import java.io.Serializable;

@Data
@EqualsAndHashCode(callSuper = false)
public class PersonExportVo implements Serializable {

    private static final long serialVersionUID = 1L;
    /**
     * 姓名
     */
    @Excel(name = "姓名", orderNum = "0", width = 15)
    private String name;
    
    /**
     * 登錄用戶名
     */
    @Excel(name = "用戶名", orderNum = "1", width = 15)
    private String username;

    @Excel(name = "手機號碼", orderNum = "2", width = 15)
    private String phoneNumber;

    /**
     * 人臉圖片
     */
    @Excel(name = "人臉圖片", orderNum = "3", width = 15, height = 30, type = 2)
    private String imageUrl;
}

3、導(dǎo)入導(dǎo)出公用工具類

import cn.afterturn.easypoi.excel.ExcelExportUtil;
import cn.afterturn.easypoi.excel.ExcelImportUtil;
import cn.afterturn.easypoi.excel.entity.ExportParams;
import cn.afterturn.easypoi.excel.entity.ImportParams;
import cn.afterturn.easypoi.excel.entity.enmus.ExcelType;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.ss.usermodel.Workbook;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URLEncoder;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;

public class ExcelUtils {
    /**
     * excel 導(dǎo)出
     *
     * @param list           數(shù)據(jù)
     * @param title          標題
     * @param sheetName      sheet名稱
     * @param pojoClass      pojo類型
     * @param fileName       文件名稱
     * @param isCreateHeader 是否創(chuàng)建表頭
     * @param response
     */
    public static void exportExcel(List<?> list, String title, String sheetName, Class<?> pojoClass, String fileName, boolean isCreateHeader, HttpServletResponse response) throws IOException {
        ExportParams exportParams = new ExportParams(title, sheetName, ExcelType.XSSF);
        exportParams.setCreateHeadRows(isCreateHeader);
        defaultExport(list, pojoClass, fileName, response, exportParams);
    }

    /**
     * excel 導(dǎo)出
     *
     * @param list      數(shù)據(jù)
     * @param title     標題
     * @param sheetName sheet名稱
     * @param pojoClass pojo類型
     * @param fileName  文件名稱
     * @param response
     */
    public static void exportExcel(List<?> list, String title, String sheetName, Class<?> pojoClass, String fileName, HttpServletResponse response) throws IOException {
        defaultExport(list, pojoClass, fileName, response, new ExportParams(title, sheetName, ExcelType.XSSF));
    }

    /**
     * excel 導(dǎo)出
     *
     * @param list         數(shù)據(jù)
     * @param pojoClass    pojo類型
     * @param fileName     文件名稱
     * @param response
     * @param exportParams 導(dǎo)出參數(shù)
     */
    public static void exportExcel(List<?> list, Class<?> pojoClass, String fileName, ExportParams exportParams, HttpServletResponse response) throws IOException {
        defaultExport(list, pojoClass, fileName, response, exportParams);
    }

    /**
     * excel 導(dǎo)出
     *
     * @param list     數(shù)據(jù)
     * @param fileName 文件名稱
     * @param response
     */
    public static void exportExcel(List<Map<String, Object>> list, String fileName, HttpServletResponse response) throws IOException {
        defaultExport(list, fileName, response);
    }

    /**
     * 默認的 excel 導(dǎo)出
     *
     * @param list         數(shù)據(jù)
     * @param pojoClass    pojo類型
     * @param fileName     文件名稱
     * @param response
     * @param exportParams 導(dǎo)出參數(shù)
     */
    private static void defaultExport(List<?> list, Class<?> pojoClass, String fileName, HttpServletResponse response, ExportParams exportParams) throws IOException {
        Workbook workbook = ExcelExportUtil.exportExcel(exportParams, pojoClass, list);
        downLoadExcel(fileName, response, workbook);
    }

    /**
     * 默認的 excel 導(dǎo)出
     *
     * @param list     數(shù)據(jù)
     * @param fileName 文件名稱
     * @param response
     */
    private static void defaultExport(List<Map<String, Object>> list, String fileName, HttpServletResponse response) throws IOException {
        Workbook workbook = ExcelExportUtil.exportExcel(list, ExcelType.HSSF);
        downLoadExcel(fileName, response, workbook);
    }

    /**
     * 下載
     *
     * @param fileName 文件名稱
     * @param response
     * @param workbook excel數(shù)據(jù)
     */
    private static void downLoadExcel(String fileName, HttpServletResponse response, Workbook workbook) throws IOException {
        try {
            response.setCharacterEncoding("UTF-8");
            response.setHeader("content-Type", "application/vnd.ms-excel");
            response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName + "." + ExcelTypeEnum.XLSX.getValue(), "UTF-8"));
            workbook.write(response.getOutputStream());
        } catch (Exception e) {
            throw new IOException(e.getMessage());
        }
    }

    /**
     * excel 導(dǎo)入
     *
     * @param filePath   excel文件路徑
     * @param titleRows  標題行
     * @param headerRows 表頭行
     * @param pojoClass  pojo類型
     * @param <T>
     * @return
     */
    public static <T> List<T> importExcel(String filePath, Integer titleRows, Integer headerRows, Class<T> pojoClass) throws IOException {
        if (StringUtils.isBlank(filePath)) {
            return null;
        }
        ImportParams params = new ImportParams();
        params.setTitleRows(titleRows);
        params.setHeadRows(headerRows);
        params.setNeedSave(true);
        params.setSaveUrl("/excel/");
        try {
            return ExcelImportUtil.importExcel(new File(filePath), pojoClass, params);
        } catch (NoSuchElementException e) {
            throw new IOException("模板不能為空");
        } catch (Exception e) {
            throw new IOException(e.getMessage());
        }
    }

    /**
     * excel 導(dǎo)入
     *
     * @param file      excel文件
     * @param pojoClass pojo類型
     * @param <T>
     * @return
     */
    public static <T> List<T> importExcel(MultipartFile file, Class<T> pojoClass) throws IOException {
        return importExcel(file, 1, 1, pojoClass);
    }

    /**
     * excel 導(dǎo)入
     *
     * @param file       excel文件
     * @param titleRows  標題行
     * @param headerRows 表頭行
     * @param pojoClass  pojo類型
     * @param <T>
     * @return
     */
    public static <T> List<T> importExcel(MultipartFile file, Integer titleRows, Integer headerRows, Class<T> pojoClass) throws IOException {
        return importExcel(file, titleRows, headerRows, false, pojoClass);
    }

    /**
     * excel 導(dǎo)入
     *
     * @param file       上傳的文件
     * @param titleRows  標題行
     * @param headerRows 表頭行
     * @param needVerfiy 是否檢驗excel內(nèi)容
     * @param pojoClass  pojo類型
     * @param <T>
     * @return
     */
    public static <T> List<T> importExcel(MultipartFile file, Integer titleRows, Integer headerRows, boolean needVerfiy, Class<T> pojoClass) throws IOException {
        if (file == null) {
            return null;
        }
        try {
            return importExcel(file.getInputStream(), titleRows, headerRows, needVerfiy, pojoClass);
        } catch (Exception e) {
            throw new IOException(e.getMessage());
        }
    }

    /**
     * excel 導(dǎo)入
     *
     * @param inputStream 文件輸入流
     * @param titleRows   標題行
     * @param headerRows  表頭行
     * @param needVerfiy  是否檢驗excel內(nèi)容
     * @param pojoClass   pojo類型
     * @param <T>
     * @return
     */
    public static <T> List<T> importExcel(InputStream inputStream, Integer titleRows, Integer headerRows, boolean needVerfiy, Class<T> pojoClass) throws IOException {
        if (inputStream == null) {
            return null;
        }
        ImportParams params = new ImportParams();
        params.setTitleRows(titleRows);
        params.setHeadRows(headerRows);
        params.setSaveUrl("/excel/");
        params.setNeedSave(true);
        params.setNeedVerfiy(needVerfiy);
        try {
            return ExcelImportUtil.importExcel(inputStream, pojoClass, params);
        } catch (NoSuchElementException e) {
            throw new IOException("excel文件不能為空");
        } catch (Exception e) {
            throw new IOException(e.getMessage());
        }
    }

    /**
     * Excel 類型枚舉
     */
    enum ExcelTypeEnum {
        XLS("xls"), XLSX("xlsx");
        private String value;

        ExcelTypeEnum(String value) {
            this.value = value;
        }

        public String getValue() {
            return value;
        }

        public void setValue(String value) {
            this.value = value;
        }
    }
}

4符相、測試

導(dǎo)出:

/**
 * 導(dǎo)出
 *
 * @param response
 */
@RequestMapping(value = "/export", method = RequestMethod.GET)
public void exportExcel(HttpServletResponse response) {
    long start = System.currentTimeMillis();
    List<MonitorPersonExportVo> personList = new ArrayList<>();
    for (int i = 0; i < 5; i++) {
        MonitorPersonExportVo personVo = new MonitorPersonExportVo();
        personVo.setName("張三" + i);
        personVo.setUsername("張三" + i);
        personVo.setPhoneNumber("18888888888");
        personVo.setImageUrl(Constant.DEFAULT_IMAGE);
        personList.add(personVo);
    }
    log.debug("導(dǎo)出excel所花時間:" + (System.currentTimeMillis() - start));
    ExcelUtils.exportExcel(personList, "員工信息表", "員工信息", MonitorPersonExportVo.class, "員工信息", response);
}
1.png

導(dǎo)入:

/**
 * 導(dǎo)入
 *
 * @param file
 */
@RequestMapping(value = "/import", method = RequestMethod.POST)
public ResultView importExcel(@RequestParam("file") MultipartFile file) {
    long start = System.currentTimeMillis();
    List<MonitorPersonImportVo> personVoList = ExcelUtils.importExcel(file, MonitorPersonImportVo.class);
    log.debug(personVoList.toString());
    log.debug("導(dǎo)入excel所花時間:" + (System.currentTimeMillis() - start));
    return getResponse(true, "導(dǎo)入成功");
}

5、注意:

SpringBoot中蠢琳,如果excel圖片文件路徑指向了SpringBoot的資源文件啊终,那么當(dāng)項目打成jar包后,easypoi將無法讀取到該資源文件傲须。

解決辦法:

重寫easypoiIFileLoader接口的實現(xiàn)類:

原代碼實現(xiàn)方式:

/**
 * Copyright 2013-2015 JueYue (qrb.jueyue@gmail.com)
 * <p>
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * <p>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p>
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package cn.afterturn.easypoi.cache.manager;

import cn.afterturn.easypoi.util.PoiPublicUtil;
import org.apache.poi.util.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.*;
import java.net.URL;
import java.net.URLConnection;

/**
 * 文件加載類,根據(jù)路徑加載指定文件
 *
 * @author JueYue
 *         2014年2月10日
 * @version 1.0
 */
public class FileLoaderImpl implements IFileLoader {

    private static final Logger LOGGER = LoggerFactory.getLogger(FileLoaderImpl.class);

    @Override
    public byte[] getFile(String url) {
        InputStream fileis = null;
        ByteArrayOutputStream baos = null;
        try {

            //判斷是否是網(wǎng)絡(luò)地址
            if (url.startsWith("http")) {
                URL urlObj = new URL(url);
                URLConnection urlConnection = urlObj.openConnection();
                urlConnection.setConnectTimeout(30);
                urlConnection.setReadTimeout(60);
                urlConnection.setDoInput(true);
                fileis = urlConnection.getInputStream();
            } else {
                //先用絕對路徑查詢,再查詢相對路徑
                try {
                    fileis = new FileInputStream(url);
                } catch (FileNotFoundException e) {
                    //獲取項目文件
                    fileis = FileLoaderImpl.class.getClassLoader().getResourceAsStream(url);
                }
            }

            baos = new ByteArrayOutputStream();
            byte[] buffer = new byte[1024];
            int len;
            while ((len = fileis.read(buffer)) > -1) {
                baos.write(buffer, 0, len);
            }
            baos.flush();
            return baos.toByteArray();
        } catch (Exception e) {
            LOGGER.error(e.getMessage(), e);
        } finally {
            IOUtils.closeQuietly(fileis);
            IOUtils.closeQuietly(baos);
        }
        LOGGER.error(fileis + "這個路徑文件沒有找到,請查詢");
        return null;
    }
}

修改后的代碼:

import cn.afterturn.easypoi.cache.manager.IFileLoader;
import org.apache.poi.util.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.net.URL;
import java.net.URLConnection;

/**
 * 文件加載類,根據(jù)路徑加載指定文件
 *
 * @author user
 * @date 2018/12/18
 */
public class FileLoaderImpl implements IFileLoader {

    private static final Logger LOGGER = LoggerFactory.getLogger(cn.afterturn.easypoi.cache.manager.FileLoaderImpl.class);

    @Override
    public byte[] getFile(String url) {
        InputStream fileis = null;
        ByteArrayOutputStream baos = null;
        try {

            //判斷是否是網(wǎng)絡(luò)地址
            if (url.startsWith("http")) {
                URL urlObj = new URL(url);
                URLConnection urlConnection = urlObj.openConnection();
                urlConnection.setConnectTimeout(30);
                urlConnection.setReadTimeout(60);
                urlConnection.setDoInput(true);
                fileis = urlConnection.getInputStream();
            } else {
                //先用絕對路徑查詢,再查詢相對路徑
                try {
                    fileis = new FileInputStream(url);
                } catch (FileNotFoundException e) {
                    //獲取項目文件
                    fileis = FileLoaderImpl.class.getClassLoader().getResourceAsStream(url);
                    if (fileis == null) {
                        fileis = FileLoaderImpl.class.getResourceAsStream(url);
                    }
                }
            }
            baos = new ByteArrayOutputStream();
            byte[] buffer = new byte[1024];
            int len;
            while ((len = fileis.read(buffer)) > -1) {
                baos.write(buffer, 0, len);
            }
            baos.flush();
            return baos.toByteArray();
        } catch (Exception e) {
            LOGGER.error(e.getMessage(), e);
        } finally {
            IOUtils.closeQuietly(fileis);
            IOUtils.closeQuietly(baos);
        }
        LOGGER.error(fileis + "這個路徑文件沒有找到,請查詢");
        return null;
    }
}

在原代碼中加入了一下代碼蓝牲,就能讀取到文件了:

if (fileis == null) {
    fileis = FileLoaderImpl.class.getResourceAsStream(url);
}

最后通過作者提供的POICacheManager的靜態(tài)方法進行設(shè)置:

public static void setFileLoder(IFileLoader fileLoder) {
    POICacheManager.fileLoder = fileLoder;
}

/**
 * 一次線程有效
 * @param fileLoder
 */
public static void setFileLoderOnce(IFileLoader fileLoder) {
    if (fileLoder != null) {
        LOCAL_FILELOADER.set(fileLoder);
    }
}

寫一個監(jiān)聽器,監(jiān)聽項目啟動時泰讽,對POICacheManager進行全局替換:

import cn.afterturn.easypoi.cache.manager.POICacheManager;
import com.***.**.framework.common.util.FileLoaderImpl;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;

@Component
public class ExcelListener implements ApplicationListener<ApplicationReadyEvent> {

    @Override
    public void onApplicationEvent(ApplicationReadyEvent event) {
        POICacheManager.setFileLoader(new FileLoaderImpl());
    }
}

源碼地址:https://gitee.com/cnovel/demo

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末例衍,一起剝皮案震驚了整個濱河市昔期,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌佛玄,老刑警劉巖硼一,帶你破解...
    沈念sama閱讀 206,311評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異梦抢,居然都是意外死亡般贼,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評論 2 382
  • 文/潘曉璐 我一進店門奥吩,熙熙樓的掌柜王于貴愁眉苦臉地迎上來哼蛆,“玉大人,你說我怎么就攤上這事霞赫∪椋” “怎么了?”我有些...
    開封第一講書人閱讀 152,671評論 0 342
  • 文/不壞的土叔 我叫張陵端衰,是天一觀的道長叠洗。 經(jīng)常有香客問我,道長靴迫,這世上最難降的妖魔是什么惕味? 我笑而不...
    開封第一講書人閱讀 55,252評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮玉锌,結(jié)果婚禮上名挥,老公的妹妹穿的比我還像新娘。我一直安慰自己主守,他們只是感情好禀倔,可當(dāng)我...
    茶點故事閱讀 64,253評論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著参淫,像睡著了一般救湖。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上涎才,一...
    開封第一講書人閱讀 49,031評論 1 285
  • 那天鞋既,我揣著相機與錄音,去河邊找鬼耍铜。 笑死邑闺,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的棕兼。 我是一名探鬼主播陡舅,決...
    沈念sama閱讀 38,340評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼伴挚!你這毒婦竟也來了靶衍?” 一聲冷哼從身側(cè)響起灾炭,我...
    開封第一講書人閱讀 36,973評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎颅眶,沒想到半個月后蜈出,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,466評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡帚呼,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,937評論 2 323
  • 正文 我和宋清朗相戀三年掏缎,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片煤杀。...
    茶點故事閱讀 38,039評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡眷蜈,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出沈自,到底是詐尸還是另有隱情酌儒,我是刑警寧澤,帶...
    沈念sama閱讀 33,701評論 4 323
  • 正文 年R本政府宣布枯途,位于F島的核電站忌怎,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏酪夷。R本人自食惡果不足惜榴啸,卻給世界環(huán)境...
    茶點故事閱讀 39,254評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望晚岭。 院中可真熱鬧鸥印,春花似錦、人聲如沸坦报。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,259評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽片择。三九已至潜的,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間字管,已是汗流浹背啰挪。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留嘲叔,地道東北人脐供。 一個月前我還...
    沈念sama閱讀 45,497評論 2 354
  • 正文 我出身青樓,卻偏偏與公主長得像借跪,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子酌壕,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,786評論 2 345

推薦閱讀更多精彩內(nèi)容