EasyExcel解析動(dòng)態(tài)表頭及導(dǎo)出

前言

excel文件導(dǎo)入及導(dǎo)出,是日常開發(fā)中經(jīng)常遇到的需求筋搏。本次筆者以EasyExcel為例仆百,針對(duì)在項(xiàng)目中遇到的動(dòng)態(tài)表頭解析及導(dǎo)出的場(chǎng)景,詳細(xì)介紹具體的代碼實(shí)現(xiàn)過程奔脐。

參考地址

https://github.com/alibaba/easyexcel

前端下載

  const download = () => {
    axios({
      method: 'GET',
      url: config.http.baseUrl + '/templateDownload',
      responseType: 'blob',
    })
      .then(function (res) {
      const content = res.data
      const blob = new Blob([content], { type: "application/application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" })
      const downloadElement = document.createElement("a");
      const href = window.URL.createObjectURL(blob);
      downloadElement.href = href;
      downloadElement.download = decodeURI(res.headers['filename']);
      document.body.appendChild(downloadElement);
      downloadElement.click();
      document.body.removeChild(downloadElement); // 下載完成移除元素
      window.URL.revokeObjectURL(href); // 釋放掉blob對(duì)象
    })
  }

模板下載

excel文件導(dǎo)入功能俄周,常常需要進(jìn)行模板下載,在springboot項(xiàng)目中髓迎,程序是以jar包的形式運(yùn)行的峦朗,所以有很多小伙伴常常

遇到在本地開發(fā)中能夠?qū)崿F(xiàn)下載功能,但部署到服務(wù)器的時(shí)候排龄,找不到模板文件的問題波势。

@Override
public void templateDownload(HttpServletResponse response, HttpServletRequest request) {
    //獲取要下載的模板名稱
    String fileName = "批量導(dǎo)入模板.xlsx";
    //獲取文件下載路徑
    String filePath = "/template/template.xlsx";
    TemplateDownloadUtil.download(response, request, fileName, filePath);
}
 import lombok.extern.slf4j.Slf4j;
import org.springframework.core.io.ClassPathResource;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URLEncoder;

/**
 * 模板文件下載工具類
 * @author 
 * @date 2021/05/20 9:20
 */
@Slf4j
public class TemplateDownloadUtil {

    public static void download(HttpServletResponse response, HttpServletRequest request,String fileName,String filePath){
        try {
            response.setContentType("application/application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
            response.setCharacterEncoding("utf-8");
            // 這里URLEncoder.encode可以防止中文亂碼 當(dāng)然和easyexcel沒有關(guān)系
            response.setHeader("Content-Disposition", "attachment; filename=" + URLEncoder.encode(fileName, "UTF-8"));
            response.setHeader("filename", URLEncoder.encode(fileName, "UTF-8"));
            response.setHeader("Access-Control-Expose-Headers", "filename,Content-Disposition");
          
            //獲取文件的路徑,此方式本地開發(fā)可以運(yùn)行橄维,服務(wù)器無法獲取文件
//            String filePath = getClass().getResource("/template/template.xlsx").getPath();
//            FileInputStream input = new FileInputStream(filePath);
          
            //在服務(wù)器中能夠讀取到模板文件
            ClassPathResource resource = new ClassPathResource(filePath);
            InputStream input = resource.getInputStream();
            OutputStream out = response.getOutputStream();
            byte[] b = new byte[2048];
            int len;
            while ((len = input.read(b)) != -1) {
                out.write(b, 0, len);
            }
            //修正 Excel在“xxx.xlsx”中發(fā)現(xiàn)不可讀取的內(nèi)容尺铣。是否恢復(fù)此工作薄的內(nèi)容?如果信任此工作簿的來源挣郭,請(qǐng)點(diǎn)擊"是"
//            response.setHeader("Content-Length", String.valueOf(input.getChannel().size()));
            input.close();
        } catch (Exception e) {
            log.error("下載模板失敗 :", e);
        }
    }

}

EasyExcel動(dòng)態(tài)表頭解析

EasyExcel簡(jiǎn)單的讀文件迄埃,官網(wǎng)中已經(jīng)有詳細(xì)的說明,本文不再贅述兑障,詳細(xì)操作參見

[官網(wǎng)] https://easyexcel.opensource.alibaba.com/docs/current/

侄非。

本文主要針對(duì)筆者遇到的復(fù)雜表頭及動(dòng)態(tài)表頭進(jìn)行講解。

模板示例

導(dǎo)入模板.png

解析

import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * 發(fā)薪單上傳excel讀取類
 *
 * @author yupf
 * @description Listener 不能被spring管理流译,要每次讀取excel都要new,然后里面用到spring可以構(gòu)造方法傳進(jìn)去
 */
@Slf4j
@Data
public class BatchReadListener extends AnalysisEventListener<Map<Integer, String>> {

    /**
     * 每隔500條存儲(chǔ)數(shù)據(jù)庫逞怨,然后清理list ,方便內(nèi)存回收
     */
    private static final int BATCH_COUNT = 500;
    //Excel數(shù)據(jù)緩存結(jié)構(gòu)
    private List<Map<Integer, Map<Integer, String>>> list = new ArrayList<>();
    //Excel表頭(列名)數(shù)據(jù)緩存結(jié)構(gòu)
    private Map<Integer, String> headTitleMap = new HashMap<>();


    /**
     * 假設(shè)這個(gè)是一個(gè)DAO福澡,當(dāng)然有業(yè)務(wù)邏輯這個(gè)也可以是一個(gè)service叠赦。當(dāng)然如果不用存儲(chǔ)這個(gè)對(duì)象沒用。
     */
    private DbFileBatchService dbFileBatchService;
    private DbFileContentService dbFileContentService;
    private FileBatch fileBatch;
    private int total = 0;

    /**
     * 如果使用了spring,請(qǐng)使用這個(gè)構(gòu)造方法。每次創(chuàng)建Listener的時(shí)候需要把spring管理的類傳進(jìn)來
     */
    public BatchReadListener(DbFileBatchService dbFileBatchService, DbFileContentService dbFileContentService, FileBatch fileBatch) {
        this.dbFileBatchService = dbFileBatchService;
        this.dbFileContentService = dbFileContentService;
        this.fileBatch = fileBatch;
    }

    /**
     * 這個(gè)每一條數(shù)據(jù)解析都會(huì)來調(diào)用
     *
     * @param data    one row value. Is is same as {@link AnalysisContext#readRowHolder()}
     * @param context
     */
    @Override
    public void invoke(Map<Integer, String> data, AnalysisContext context) {
        log.info("解析到一條數(shù)據(jù):{}", JSON.toJSONString(data));
        total++;
        Map<Integer, Map<Integer, String>> map = new HashMap<>();
        map.put(context.readRowHolder().getRowIndex(), data);
        list.add(map);
        // 達(dá)到BATCH_COUNT了除秀,需要去存儲(chǔ)一次數(shù)據(jù)庫糯累,防止數(shù)據(jù)幾萬條數(shù)據(jù)在內(nèi)存,容易OOM
        if (list.size() >= BATCH_COUNT) {
            saveData();
            // 存儲(chǔ)完成清理 list
            list.clear();
        }
    }

    /**
     * 所有數(shù)據(jù)解析完成了 都會(huì)來調(diào)用
     *
     * @param context
     */
    @Override
    public void doAfterAllAnalysed(AnalysisContext context) {
        // 這里也要保存數(shù)據(jù)册踩,確保最后遺留的數(shù)據(jù)也存儲(chǔ)到數(shù)據(jù)庫
        saveData();
        log.info("所有數(shù)據(jù)解析完成泳姐!");
    }

    /**
     * 解析表頭數(shù)據(jù)
     **/
    @Override
    public void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) {
        log.info("表頭數(shù)據(jù):{}", JSONObject.toJSONString(headMap));
        headTitleMap = headMap;
    }

    /**
     * 加上存儲(chǔ)數(shù)據(jù)庫
     */
    private void saveData() {
        log.info("{}條數(shù)據(jù),開始存儲(chǔ)數(shù)據(jù)庫暂吉!", list.size());
        FileContent fileContent = null;
        List<FileContent> fileContentList = list.stream().flatMap(
            integerMap -> integerMap.entrySet().stream().map(entrySet -> {
                //entrySet.getKey()獲取的是內(nèi)容的RowIndex,實(shí)際的行數(shù)需要根據(jù)表頭數(shù)進(jìn)行處理
                Integer rowIndex = entrySet.getKey();
                Map<Integer, String> value = entrySet.getValue();
                log.info(JSONObject.toJSONString(value));
                fileContent = new FileContent();
                fileContent.setBatchId(fileBatch.getId());
                fileContent.setBatchNo(fileBatch.getBatchNo());
                //固定字段入庫
                fileContent.setName(value.get(0) != null ? value.get(0).trim() : "");
                fileContent.setCertNo(value.get(1) != null ? value.get(1).trim() : "");
                fileContent.setRealAmount(value.get(2) != null ? value.get(2).trim() : "");
                //所有動(dòng)態(tài)表頭數(shù)據(jù)轉(zhuǎn)為JSON串入庫
                fileContent.setFieldsValue(JSONObject.toJSONString(value));
                //取實(shí)際的內(nèi)容rowIndex
                fileContent.setRowNum(rowIndex + 1);
                fileContent.setCreateTime(LocalDateTime.now());
                return xcSalaryFileContent;
            }
        )).collect(Collectors.toList());
        log.info(JSONObject.toJSONString(fileContentList));
        dbFileContentService.saveBatch(fileContentList);
        log.info("存儲(chǔ)數(shù)據(jù)庫成功胖秒!");
    }

}
BatchReadListener listener = new BatchReadListener(dbFileBatchService, dbFileContentService, fileBatch);
    try {
        //注:headRowNumber默認(rèn)為1,現(xiàn)賦值為2慕的,即從第三行開始讀取內(nèi)容
        EasyExcel.read(fileInputStream, listener).headRowNumber(2).sheet().doRead();
    } catch (Exception e) {
        log.info("EasyExcel解析文件失敗阎肝,{}", e);
        throw new CustomException("文件解析失敗,請(qǐng)重新上傳");
    }
    //獲取表頭信息進(jìn)行處理
    Map<Integer, String> headTitleMap = listener.getHeadTitleMap();
    //獲取動(dòng)態(tài)表頭信息
    List<String> headList = headTitleMap.keySet().stream().map(key -> {
        String head = headTitleMap.get(key);
        log.info(head);
        return head == null ? "" : head.replace("*", "");
    }).collect(Collectors.toList());
    //可以對(duì)表頭進(jìn)行入庫保存肮街,方便后續(xù)導(dǎo)出

綜上风题,動(dòng)態(tài)表頭即可完成解析。

EasyExcel動(dòng)態(tài)表頭導(dǎo)出

導(dǎo)出示例

失敗信息導(dǎo)出.png

獲取動(dòng)態(tài)頭

     private List<List<String>> getFileHeadList( FileBatch fileBatch) {
         String head = fileBatch.getFileHead();
         List<String> headList = Arrays.asList(head.split(","));
         List<List<String>> fileHead = headList.stream().map(item ->    concatHead(Lists.newArrayList(item))).collect(Collectors.toList());
         fileHead.add(concatHead(Lists.newArrayList("備注")));
         return fileHead;
     }
    /**
     * 填寫須知
     * @param headContent
     * @return
     */
    private List<String> concatHead(List<String> headContent) {
        String remake = "填寫須知:                                                                                                \n" +
                "1.系統(tǒng)自動(dòng)識(shí)別Excel表格嫉父,表頭必須含有“企業(yè)賬戶號(hào)”俯邓、“企業(yè)賬戶名”、“實(shí)發(fā)金額”熔号;\n" +
                "2.帶 “*” 為必填字段,填寫后才能上傳成功鸟整;\n" +
                "3.若需上傳其他表頭引镊,可自行在“實(shí)發(fā)金額”后添加表頭,表頭最多可添加20個(gè)篮条,表頭名稱請(qǐng)控制在8個(gè)字以內(nèi)弟头;\n" +
                "4.填寫的表頭內(nèi)容不可超過30個(gè)字;\n" +
                "5.實(shí)發(fā)金額支持填寫到2位小數(shù)涉茧;\n" +
                "6.每次導(dǎo)入數(shù)據(jù)不超過5000條赴恨。\n" +
                "\n" +
                "注:請(qǐng)勿刪除填寫須知,刪除后將導(dǎo)致文件上傳失敗\n" +
                "\n" +
                "表頭示例:";
        headContent.add(0, remake);
        return headContent;
    }

獲取數(shù)據(jù)

    List<FileContent> fileContentList = dbFileContentService.list(
        Wrappers.<FileContent>lambdaQuery()
        .eq(FileContent::getBatchId, fileBatch.getId())
        .orderByAsc(FileContent::getRowNum)
    );
    List<List<Object>> contentList = fileContentList.stream().map(fileContent -> {
        List<Object> rowList = new ArrayList<>();
        String fieldsValue = fileContent.getFieldsValue();
        JSONObject contentObj = JSONObject.parseObject(fieldsValue);
        for (int columnIndex = 0 , length = headList.size(); columnIndex < length; columnIndex++) {
            Object content = contentObj.get(columnIndex);
            rowList.add(content == null ? "" : content);
        }
        rowList.add(fileContent.getCheckMessage());
        return rowList;
    }).collect(Collectors.toList());

單元格格式設(shè)置

import com.alibaba.excel.metadata.data.DataFormatData;
import com.alibaba.excel.metadata.data.WriteCellData;
import com.alibaba.excel.write.handler.context.CellWriteHandlerContext;
import com.alibaba.excel.write.metadata.style.WriteCellStyle;
import com.alibaba.excel.write.metadata.style.WriteFont;
import com.alibaba.excel.write.style.HorizontalCellStyleStrategy;
import org.apache.poi.ss.usermodel.BorderStyle;
import org.apache.poi.ss.usermodel.HorizontalAlignment;
import org.apache.poi.ss.usermodel.IndexedColors;

import java.util.List;

/**
 * 設(shè)置表頭和填充內(nèi)容的樣式
 */
public class CellStyleStrategy extends HorizontalCellStyleStrategy {

    private final WriteCellStyle headWriteCellStyle;
    private final WriteCellStyle contentWriteCellStyle;

    /**
     * 操作列
     */
    private final List<Integer> columnIndexes;

    public CellStyleStrategy(List<Integer> columnIndexes,WriteCellStyle headWriteCellStyle, WriteCellStyle contentWriteCellStyle) {
        this.columnIndexes = columnIndexes;
        this.headWriteCellStyle = headWriteCellStyle;
        this.contentWriteCellStyle = contentWriteCellStyle;
    }

    //設(shè)置頭樣式
    @Override
    protected void setHeadCellStyle( CellWriteHandlerContext context) {
        // 獲取字體實(shí)例
        WriteFont headWriteFont = new WriteFont();
        headWriteFont.setFontName("宋體");
        //表頭不同處理
        if (columnIndexes.get(0).equals(context.getRowIndex())) {
            headWriteCellStyle.setFillForegroundColor(IndexedColors.WHITE.getIndex());
            headWriteCellStyle.setHorizontalAlignment(HorizontalAlignment.LEFT);
            headWriteFont.setFontHeightInPoints((short) 12);
            headWriteFont.setBold(false);
            headWriteFont.setFontName("宋體");
        }else{
            headWriteCellStyle.setFillForegroundColor(IndexedColors.GREY_25_PERCENT.getIndex());
            headWriteCellStyle.setHorizontalAlignment(HorizontalAlignment.CENTER);
            headWriteFont.setFontHeightInPoints((short) 11);
            headWriteFont.setBold(false);
            headWriteFont.setFontName("微軟雅黑");
        }
        headWriteCellStyle.setWriteFont(headWriteFont);
        DataFormatData dataFormatData = new DataFormatData();
        dataFormatData.setIndex((short)49);
        headWriteCellStyle.setDataFormatData(dataFormatData);
        if (stopProcessing(context)) {
            return;
        }
        WriteCellData<?> cellData = context.getFirstCellData();
        WriteCellStyle.merge(headWriteCellStyle, cellData.getOrCreateStyle());
    }

    //設(shè)置填充數(shù)據(jù)樣式
    @Override
    protected void setContentCellStyle(CellWriteHandlerContext context) {
        WriteFont contentWriteFont = new WriteFont();
        contentWriteFont.setFontName("宋體");
        contentWriteFont.setFontHeightInPoints((short) 11);
        //設(shè)置數(shù)據(jù)填充后的實(shí)線邊框
        contentWriteCellStyle.setWriteFont(contentWriteFont);
        contentWriteCellStyle.setBorderLeft(BorderStyle.THIN);
        contentWriteCellStyle.setBorderTop(BorderStyle.THIN);
        contentWriteCellStyle.setBorderRight(BorderStyle.THIN);
        contentWriteCellStyle.setBorderBottom(BorderStyle.THIN);
        DataFormatData dataFormatData = new DataFormatData();
        dataFormatData.setIndex((short)49);
        contentWriteCellStyle.setDataFormatData(dataFormatData);
        contentWriteCellStyle.setHorizontalAlignment(HorizontalAlignment.CENTER);
        WriteCellData<?> cellData = context.getFirstCellData();
        WriteCellStyle.merge(contentWriteCellStyle, cellData.getOrCreateStyle());
    }
}

行高設(shè)置

import com.alibaba.excel.write.style.row.AbstractRowHeightStyleStrategy;
import org.apache.poi.ss.usermodel.Row;

/**
 * 設(shè)置表頭的自動(dòng)調(diào)整行高策略
 */
public class CellRowHeightStyleStrategy extends AbstractRowHeightStyleStrategy {

    @Override
    protected void setHeadColumnHeight(Row row, int relativeRowIndex) {
        //設(shè)置主標(biāo)題行高為17.7
        if(relativeRowIndex == 0){
            //如果excel需要顯示行高為15伴栓,那這里就要設(shè)置為15*20=300
            row.setHeight((short) 3240);
        }
    }

    @Override
    protected void setContentColumnHeight(Row row, int relativeRowIndex) {
    }
}

列寬度自適應(yīng)

如果是簡(jiǎn)單表頭伦连,可以使用EasyExcel中的LongestMatchColumnWidthStyleStrategy()來實(shí)現(xiàn)。

EasyExcel.write(fileName, LongestMatchColumnWidthData.class)
    .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy()).sheet("模板").doWrite(dataLong());

如果是復(fù)雜表頭钳垮,就需要自己來實(shí)現(xiàn)惑淳,代碼如下:


import com.alibaba.excel.enums.CellDataTypeEnum;
import com.alibaba.excel.metadata.Head;
import com.alibaba.excel.metadata.data.CellData;
import com.alibaba.excel.metadata.data.WriteCellData;
import com.alibaba.excel.write.metadata.holder.WriteSheetHolder;
import com.alibaba.excel.write.style.column.AbstractColumnWidthStyleStrategy;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.poi.ss.usermodel.Cell;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author yupf
 * @description
 * @date 2022/9/7 18:48
 */
@Slf4j
public class CellWidthStyleStrategy extends AbstractColumnWidthStyleStrategy {
    private Map<Integer, Map<Integer, Integer>> CACHE = new HashMap<>();

    @Override
    protected void setColumnWidth(WriteSheetHolder writeSheetHolder, List<WriteCellData<?>> cellDataList, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) {
        Map<Integer, Integer> maxColumnWidthMap = CACHE.get(writeSheetHolder.getSheetNo());
        if (maxColumnWidthMap == null) {
            maxColumnWidthMap = new HashMap<>();
            CACHE.put(writeSheetHolder.getSheetNo(), maxColumnWidthMap);
        }
        if (isHead) {
            if(relativeRowIndex.intValue() == 1){
                Integer length = cell.getStringCellValue().getBytes().length;
                Integer maxColumnWidth = maxColumnWidthMap.get(cell.getColumnIndex());
                if (maxColumnWidth == null || length > maxColumnWidth) {
                    maxColumnWidthMap.put(cell.getColumnIndex(), length);
                    writeSheetHolder.getSheet().setColumnWidth(cell.getColumnIndex(), length * 300);
                }
            }
        }else{
            Integer columnWidth = this.dataLength(cellDataList, cell, isHead);
            if (columnWidth >= 0) {
                if (columnWidth > 255) {
                    columnWidth = 255;
                }
                Integer maxColumnWidth = maxColumnWidthMap.get(cell.getColumnIndex());
                if (maxColumnWidth == null || columnWidth > maxColumnWidth) {
                    maxColumnWidthMap.put(cell.getColumnIndex(), columnWidth);
                    writeSheetHolder.getSheet().setColumnWidth(cell.getColumnIndex(), columnWidth * 256);
                }
            }
        }
    }

    private Integer dataLength(List<WriteCellData<?>> cellDataList, Cell cell, Boolean isHead) {
        if (isHead) {
            return cell.getStringCellValue().getBytes().length;
        } else {
            CellData cellData = cellDataList.get(0);
            CellDataTypeEnum type = cellData.getType();
            if (type == null) {
                return -1;
            } else {
                switch (type) {
                    case STRING:
                        return cellData.getStringValue().getBytes().length;
                    case BOOLEAN:
                        return cellData.getBooleanValue().toString().getBytes().length;
                    case NUMBER:
                        return cellData.getNumberValue().toString().getBytes().length;
                    default:
                        return -1;
                }
            }
        }
    }
}

寫入文件

EasyExcel.write(response.getOutputStream())
    .head(head)
    .registerWriteHandler(new CellRowHeightStyleStrategy())   //設(shè)置行高的策略
    .registerWriteHandler(new CellStyleStrategy(Arrays.asList(0,1),new WriteCellStyle(), new WriteCellStyle()))
    .registerWriteHandler(new CellWidthStyleStrategy())
    .sheet(sheetName)
    .doWrite(list);

總結(jié)

以上便是EasyExcel解析動(dòng)態(tài)表頭及導(dǎo)出的整個(gè)過程。
在使用過程中椿息,筆者的感受是蜕便,上手難度很低惦辛,很適合新手去做簡(jiǎn)單的表格解析咬腋,當(dāng)然绢馍,如果你的需求有復(fù)雜的格式向瓷,EasyExcel也提供了api,能夠很好的滿足需要舰涌。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末猖任,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子舵稠,更是在濱河造成了極大的恐慌超升,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,542評(píng)論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件哺徊,死亡現(xiàn)場(chǎng)離奇詭異室琢,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)落追,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,596評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門盈滴,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人轿钠,你說我怎么就攤上這事巢钓。” “怎么了疗垛?”我有些...
    開封第一講書人閱讀 158,021評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵症汹,是天一觀的道長。 經(jīng)常有香客問我贷腕,道長背镇,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,682評(píng)論 1 284
  • 正文 為了忘掉前任泽裳,我火速辦了婚禮瞒斩,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘涮总。我一直安慰自己胸囱,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,792評(píng)論 6 386
  • 文/花漫 我一把揭開白布瀑梗。 她就那樣靜靜地躺著烹笔,像睡著了一般。 火紅的嫁衣襯著肌膚如雪抛丽。 梳的紋絲不亂的頭發(fā)上箕宙,一...
    開封第一講書人閱讀 49,985評(píng)論 1 291
  • 那天,我揣著相機(jī)與錄音铺纽,去河邊找鬼柬帕。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的陷寝。 我是一名探鬼主播锅很,決...
    沈念sama閱讀 39,107評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼凤跑!你這毒婦竟也來了爆安?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,845評(píng)論 0 268
  • 序言:老撾萬榮一對(duì)情侶失蹤仔引,失蹤者是張志新(化名)和其女友劉穎扔仓,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體咖耘,經(jīng)...
    沈念sama閱讀 44,299評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡翘簇,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,612評(píng)論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了儿倒。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片版保。...
    茶點(diǎn)故事閱讀 38,747評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖夫否,靈堂內(nèi)的尸體忽然破棺而出彻犁,到底是詐尸還是另有隱情,我是刑警寧澤凰慈,帶...
    沈念sama閱讀 34,441評(píng)論 4 333
  • 正文 年R本政府宣布汞幢,位于F島的核電站,受9級(jí)特大地震影響微谓,放射性物質(zhì)發(fā)生泄漏急鳄。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,072評(píng)論 3 317
  • 文/蒙蒙 一堰酿、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧张足,春花似錦触创、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,828評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至碉咆,卻和暖如春抖韩,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背疫铜。 一陣腳步聲響...
    開封第一講書人閱讀 32,069評(píng)論 1 267
  • 我被黑心中介騙來泰國打工茂浮, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,545評(píng)論 2 362
  • 正文 我出身青樓席揽,卻偏偏與公主長得像顽馋,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子幌羞,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,658評(píng)論 2 350

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