day06项目【整合阿里云OSS和Excel导入分类】

1 整合阿里云OSS

01-阿里云存储OSS

一、对象存储OSS

为了解决海量数据存储与弹性扩容,项目中我们采用云存储的解决方案- 阿里云OSS。 

1、开通“对象存储OSS”服务

(1)申请阿里云账号

(2)实名认证

(3)开通“对象存储OSS”服务

(4)进入管理控制台

2、创建Bucket

选择:标准存储、公共读、不开通

3、上传默认头像

创建文件夹avatar,上传默认的用户头像






02-后端集成OSS

一、新建云存储微服务

1、在service模块下创建子模块service-oss

2、配置pom.xml

service-oss上级模块service已经引入service的公共依赖,所以service-oss模块只需引入阿里云oss相关依赖即可,

service父模块已经引入了service-base模块,所以Swagger相关默认已经引入

<dependencies>

        <!-- 阿里云oss依赖 -->

        <dependency>

            <groupId>com.aliyun.oss</groupId>

            <artifactId>aliyun-sdk-oss</artifactId>

        </dependency>

        <!-- 日期工具栏依赖 -->

        <dependency>

            <groupId>joda-time</groupId>

            <artifactId>joda-time</artifactId>

        </dependency>

    </dependencies>

3、配置application.properties

#服务端口

server.port=8002

#服务名

spring.application.name=service-oss

#环境设置:dev、test、prod

spring.profiles.active=dev

#阿里云OSS

#不同的服务器,地址不同

aliyun.oss.file.endpoint=your endpoint

aliyun.oss.file.keyid=your accessKeyId

aliyun.oss.file.keysecret=your accessKeySecret

#bucket可以在控制台创建,也可以使用java代码创建

aliyun.oss.file.bucketname=guli-file

4、logback-spring.xml


5、创建启动类

创建OssApplication.java

package com.atguigu.oss;

import org.springframework.boot.SpringApplication;

import org.springframework.boot.autoconfigure.SpringBootApplication;

import org.springframework.context.annotation.ComponentScan;

@SpringBootApplication

@ComponentScan(basePackages = {"com.atguigu"})

public class OssApplication {

    public static void main(String[] args) {

        SpringApplication.run(OssApplication.class,args);

    }

}

6、启动项目

报错

spring boot 会默认加载org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration这个类,

而DataSourceAutoConfiguration类使用了@Configuration注解向spring注入了dataSource bean,又因为项目(oss模块)中并没有关于dataSource相关的配置信息,所以当spring创建dataSource bean时因缺少相关的信息就会报错。

@SpringBootApplication(exclude=DataSourceAutoConfiguration.class)

即可成功:

二、实现文件上传

1、从配置文件读取常量

创建常量读取工具类:ConstantPropertiesUtil.java

使用@Value读取application.properties里的配置内容

用spring的 InitializingBean 的 afterPropertiesSet 来初始化配置信息,这个方法将在所有的属性被初始化后调用。

//当项目已启动,spring接口,spring加载之后,执行接口一个方法

@Component

public class ConstantPropertiesUtil implements InitializingBean {

    //读取配置文件内容

    @Value("${aliyun.oss.file.endpoint}")

    private String endpoint;

    @Value("${aliyun.oss.file.keyid}")

    private String keyId;

    @Value("${aliyun.oss.file.keysecret}")

    private String keySecret;

    @Value("${aliyun.oss.file.bucketname}")

    private String bucketName;


    //定义公开静态常量

    public static String END_POINT;

    public static String ACCESS_KEY_ID;

    public static String ACCESS_KEY_SECRET;

    public static String BUCKET_NAME;


    @Override

    public void afterPropertiesSet()throws Exception {

        END_POINT =endpoint;

        ACCESS_KEY_ID =keyId;

        ACCESS_KEY_SECRET =keySecret;

        BUCKET_NAME =bucketName;

    }

}

2、文件上传

创建Service接口:uploadFileAvatar.java

public interface OssService {

    String uploadFileAvatar(MultipartFile file);

}

实现:OssServiceImpl.java

参考SDK中的:Java->上传文件->简单上传->流式上传->上传文件流

@Service

public class OssServiceImpl implements OssService {

    //上传头像到oss

    @Override

    public String uploadFileAvatar(MultipartFile file) {

        // Endpoint以杭州为例,其它Region请按实际情况填写。

        String endpoint =ConstantPropertiesUtil.END_POINT;

        String accessKeyId =ConstantPropertiesUtil.ACCESS_KEY_ID;

        String accessKeySecret =ConstantPropertiesUtil.ACCESS_KEY_SECRET;

        String bucketName =ConstantPropertiesUtil.BUCKET_NAME;


        try {

            // 创建OSSClient实例。

            OSS ossClient =new OSSClientBuilder().build(endpoint,accessKeyId,accessKeySecret);

            // 上传文件流。

            InputStream inputStream = file.getInputStream();

            //获取文件名称

            String fileName = file.getOriginalFilename();

            //调用oss方法实现上传

            //第一个参数 Bucket名称

            //第二个参数 上传到oss文件路径和文件名称 如/aa/bb/1.jpg

            //第三个参数 上传文件输入流

            ossClient.putObject(bucketName,fileName,inputStream);

            // 关闭OSSClient。

            ossClient.shutdown();


           //把上传之后文件的路径返回

            //需要把上传到阿里云oss路径手动拼接出来

            //https://guli-file-1010.oss-cn-beijing.aliyuncs.com/11.jpg

            String url ="https://"+bucketName+"."+endpoint+"/"+fileName;

            return url;

        }catch (IOException e) {

            e.printStackTrace();

            return null;

        }

    }

}

3、控制层

创建controller:FileUploadController.java

@Api(description="阿里云文件管理")

@RestController

@RequestMapping("/eduoss/fileoss")

@CrossOrigin

public class OssController {

    @Autowired

    private OssService ossService;


    //上传头像的方法

    @ApiOperation(value ="文件上传")

    @PostMapping

    public R uploadOssFile(@ApiParam(name ="file", value ="文件", required =true)

                                                         @RequestParam("file")MultipartFile file){

        //获取上传文件

        //返回上传到oss的路径

        String url =ossService.uploadFileAvatar(file);

        return R.ok().data("url",url);

    }

}

4、重启oss服务

5、Swagger中测试文件上传

解决上传文件覆盖问题:

//1 在文件名称里面添加随机唯一的值

String uuid =UUID.randomUUID().toString().replaceAll("-","");

//如qwadsfadeasd01.jpg

fileName =uuid+fileName;


//2 把文件按照日期进行分类

//2020/11/12/01.jpg

//获取当前日期

String datePath =new DateTime().toString("yyyy/MM/dd");

//拼接

//如2020/11/12/qwadsfadeasd01.jpg

fileName =datePath +"/" + fileName;

测试:

6、配置nginx反向代理

配置nginx实现请求转发的功能:

验证:



03-前端整合上传组件

一、前端整合图片上传组件

1、复制头像上传组件

从vue-element-admin复制组件:

vue-element-admin/src/components/ImageCropper

vue-element-admin/src/components/PanThumb

2、前端参考实现

src/views/components-demo/avatarUpload.vue

3、前端添加文件上传组件

src/views/edu/teacher/save.vue

template:


引入组件模块:

import ImageCropper from '@/components/ImageCropper'

import PanThumb from '@/components/PanThumb'

4、设置默认头像(也可不设置)

onfig/dev.env.js中添加阿里云oss bucket地址

OSS_PATH:'"https://guli-file.oss-cn-beijing.aliyuncs.com"'

组件中初始化头像默认地址

const defaultForm = {

  ......,

  avatar: process.env.OSS_PATH + '/avatar/default.jpg'

}

5、js脚本实现上传和图片回显

export default {

    components: { ImageCropper, PanThumb },

    data(){

        return {

            teacher:{

.........

            },

            //上传弹框组件是否显示

            imagecropperShow:false,

            imagecropperKey:0,//上传组件key值

            BASE_API: process.env.BASE_API, // 接口API地址

            saveBtnDisabled: false // 保存按钮是否禁用,

        }

    },

   ......,

    methods: {

        //其他函数

        ......,

        close(){//关闭上传弹框方法

            this.imagecropperShow = false;

            // 上传失败后,重新打开上传组件时初始化组件,否则显示上一次的上传结果

            this.imagecropperKey = this.imagecropperKey + 1

        },

        cropSuccess(data){//上传成功方法

            this.imagecropperShow = false;

            //上传之后接口返回图片地址

            this.teacher.avatar = data.url

            // 上传成功后,重新打开上传组件时初始化组件,否则显示上一次的上传结果

            this.imagecropperKey = this.imagecropperKey + 1

        },

二、测试文件上传

前后端联调



2 EasyExcel导入课程分类

01-EasyExcel读写Excel的基本使用

一、Excel导入导出的应用场景

1、数据导入:减轻录入工作量

2、数据导出:统计信息归档

3、数据传输:异构系统之间数据传输

二、EasyExcel简介

1、EasyExcel特点

    Java领域解析、生成Excel比较有名的框架有Apache poi、jxl等。但他们都存在一个严重的问题就是非常的耗内存。如果你的系统并发量不大的话可能还行,但是一旦并发上来后一定会OOM或者JVM频繁的full gc。

    EasyExcel是阿里巴巴开源的一个excel处理框架,以使用简单、节省内存著称。EasyExcel能大大减少占用内存的主要原因是在解析Excel时没有将文件数据一次性全部加载到内存中,而是从磁盘上一行行读取数据,逐个解析。

    EasyExcel采用一行一行的解析模式,并将一行的解析结果以观察者的模式通知处理(AnalysisEventListener)。



02-Excel写

一、创建项目,实现EasyExcel对Excel写操作

1、创建一个普通的maven项目

项目名:excel-easydemo

2、pom中引入xml相关依赖

<dependencies>

        <dependency>

            <groupId>com.alibaba</groupId>

            <artifactId>easyexcel</artifactId>

            <version>2.1.1</version>

        </dependency>

    </dependencies>

3、创建实体类

设置表头和添加的数据字段

@Data

public class DemoData {

    //设置excel表头名称

    @ExcelProperty("学生编号")

    private Integer sno;

    @ExcelProperty("学生姓名")

    private String sname;

}

4 、实现写操作

TestEasyExcel.java

(1)创建方法循环设置要添加到Excel的数据

//循环设置要添加的数据,最终封装到list集合中

private static List<DemoData> getData(){

        List<DemoData> list = new ArrayList<>();

        for (int i=0;i<10;i++){

            DemoData data = new DemoData();

            data.setSno(i);

            data.setSname("lucy"+i);

            list.add(data);

        }

        return list;

    }

(2)实现最终的添加操作(写法一)

public static void main(String[] args) {

        //实现excel写的操作

        //1 设置写入文件夹地址和excel文件名称

        String filename = "F:\\write.xlsx";

        //2 调用easyexcel里面的方法实现写操作

        //write方法两个参数:第一个参数文件路径名称,第二个参数实体类class

        EasyExcel.write(filename,DemoData.class).sheet("学生列表").doWrite(getData());

    }

(3)实现最终的添加操作(写法二)

public static void main(String[] args) throws Exception {

        // 写法2,方法二需要手动关闭流

        //实现excel写的操作

        //1 设置写入文件夹地址和excel文件名称

        String filename = "F:\\write.xlsx";

        ExcelWriter excelWriter=EasyExcel.write(fileName,DemoData.class).build();

        WriteSheet writeSheet=EasyExcel.writerSheet("写入方法二").build();

        excelWriter.write(data(),writeSheet);

        /// 千万别忘记finish 会帮忙关闭流

        excelWriter.finish();

    }


03-Excel读

一、实现EasyExcel对Excel读操作

1、创建实体类

@Data

public class DemoData {

    //设置excel表头名称

    @ExcelProperty(value = "学生编号",index = 0)

    private Integer sno;

    @ExcelProperty(value = "学生姓名",index = 1)

    private String sname;

}

2、创建读取操作的监听器

public class ExcelListener extends AnalysisEventListener<DemoData> {

    //一行一行读取excel内容

    @Override

    public void invoke(DemoData demoData, AnalysisContext analysisContext) {

        System.out.println("******"+demoData);

    }

    //读取表头内容

    @Override

    public void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) {

        System.out.println("表头:"+headMap);

    }

    //读取完成之后

    @Override

    public void doAfterAllAnalysed(AnalysisContext analysisContext) {

    }

}

3、调用实现最终的读取

public class TestEasyExcel {

    public static void main(String[] args) {

        //实现excel读操作

        // 写法1:

        String filename = "F:\\write.xlsx";

        // 这里 需要指定读用哪个class去读,然后读取第一个sheet 文件流会自动关闭

        EasyExcel.read(filename,DemoData.class,new ExcelListener()).sheet().doRead();

        // 写法2:

        InputStream in = new BufferedInputStream(new FileInputStream("F:\\01.xlsx"));

        ExcelReader excelReader = EasyExcel.read(in, DemoData.class, new ExcelListener()).build();

        ReadSheet readSheet = EasyExcel.readSheet(0).build();

        excelReader.read(readSheet);

        // 这里千万别忘记关闭,读的时候会创建临时文件,到时磁盘会崩的

        excelReader.finish();

    }


04-前端页面的实现

一、Excel模板(本案例中使用静态模板)

1、编辑Excel模板

2、将文件上传至阿里云OSS

二、配置路由

1、添加路由

{

    path: '/subject',

    component: Layout,

    redirect: '/subject/table',

    name: '课程分类管理',

    meta: { title: '课程分类管理', icon: 'example' },

    children: [

      {

        path: 'table',

        name: '课程分类列表',

        component: () => import('@/views/edu/subject/list'),

        meta: { title: '课程分类列表', icon: 'table' }

      },

      {

        path: 'tree',

        name: '添加课程分类',

        component: () => import('@/views/edu/subject/save'),

        meta: { title: '添加课程分类', icon: 'tree' }

      }

    ]

  },

2、添加vue组件

三、表单组件save.vue

1、js定义数据

<script>

export default {

    data(){

        return {

            BASE_API: process.env.BASE_API, // 接口API地址

            importBtnDisabled: false, // 按钮是否禁用,

            loading: false

        }

    },

2、template

<template>

  <div class="app-container">

    <el-form label-width="120px">

      <el-form-item label="信息描述">

        <el-tag type="info">excel模版说明</el-tag>

        <el-tag>

          <i class="el-icon-download"/>

          <a :href="'/static/01.xlsx'">点击下载模版</a>

        </el-tag>

      </el-form-item>


      <el-form-item label="选择Excel">

        <el-upload

          ref="upload"    //相当于id='upload'

          :auto-upload="false"

          :on-success="fileUploadSuccess"

          :on-error="fileUploadError"

          :disabled="importBtnDisabled"

          :limit="1"  //限制每次只能上传一个文件

          :action="BASE_API+'/eduservice/edu-subject/addSubject'"

          name="file"

          accept="application/vnd.ms-excel">

          <el-button slot="trigger" size="small" type="primary">选取文件</el-button>

          <el-button

            :loading="loading"

            style="margin-left: 10px;"

            size="small"

            type="success"

            @click="submitUpload">上传到服务器</el-button>

        </el-upload>

      </el-form-item>

    </el-form>

  </div>

</template>

3、js上传方法

methods:{

        //点击按钮上传文件到接口里面

        submitUpload(){

            this.importBtnDisabled = true

            this.loading = true

            //js:document.getElementById("upload").submit()

            this.$refs.upload.submit()

        },

        ........

    }

4、回调函数

        //上传成功

        fileUploadSuccess(){

            //提示信息

            this.loading = false

            this.$message({

                type: 'success',

                message:'添加课程分类成功'

            })

            //跳转课程分类列表

        },

        //上传失败

        fileUploadError(){

            this.loading = false

            this.$message({

                type:'error',

                message:'添加课程失败'

            })

        }



05-课程分类管理接口

一、添加依赖

1、service-edu模块配置依赖

<dependencies>

        <dependency>

            <groupId>com.alibaba</groupId>

            <artifactId>easyexcel</artifactId>

            <version>2.1.1</version>

        </dependency>

    </dependencies>

二、业务处理

1、EduSubjectController

@Api(description="课程分类管理")

@RestController

@RequestMapping("/eduservice/edu-subject")

@CrossOrigin //跨域

public class EduSubjectController {

    @Autowired

    private EduSubjectService subjectService;

    //添加课程分类

    //获取上传过来的文件把文件内容读取出来

    @PostMapping("addSubject")

    public R addSubject(MultipartFile file){

        //上传过来的excel文件

        subjectService.saveSubject(file,subjectService);

        return R.ok();

    }

}

2、创建和Excel对应的实体类

@Data

public class SubjectData {

    @ExcelProperty(index = 0)

    private String oneSubjectName;

    @ExcelProperty(index = 1)

    private String twoSubjectName;

}

3、EduSubjectService

(1)接口

public interface EduSubjectService extends IService<EduSubject> {

    //添加课程分类

    void saveSubject(MultipartFile file,EduSubjectService subjectService);

}

(2)实现类

@Service

public class EduSubjectServiceImpl extends ServiceImpl<EduSubjectMapper, EduSubject> implements EduSubjectService {

    //添加课程分类

    @Override

    public void saveSubject(MultipartFile file,EduSubjectService subjectService) {

        try {

            //文件输入流

            InputStream in = file.getInputStream();

            //调用方法进行读取

            EasyExcel.read(in, SubjectData.class,new SubjectExcelListener(subjectService)).sheet().doRead();

        } catch (IOException e) {

            e.printStackTrace();

        }

    }

}

4、创建读取Excel监听器

public class SubjectExcelListener extends AnalysisEventListener<SubjectData> {

    //因为SubjectExcelListener不能交给spring进行管理,需要自己new,不能注入其他对象

    //不能实现数据库操作

    public EduSubjectService subjectService;

    public SubjectExcelListener() {}

    public SubjectExcelListener(EduSubjectService subjectService) {

        this.subjectService = subjectService;

    }


    //读取excel内容,一行一行进行读取

    @Override

    public void invoke(SubjectData subjectData, AnalysisContext analysisContext) {

        if (subjectData == null){

            throw new GuliException(20001,"文件数据为空");

        }

        //一行一行读取,每次读取有两个值,第一个值一级分类,第二个值二级分类

        //判断一级分类是否重复

        EduSubject exitOneSubject = this.exitOneSubject(subjectService, subjectData.getOneSubjectName());

        if (exitOneSubject == null){//没有相同一级分类,进行添加

            exitOneSubject = new EduSubject();

            exitOneSubject.setParentId("0");

            exitOneSubject.setTitle(subjectData.getOneSubjectName()); //一级分类名称

            subjectService.save(exitOneSubject);

        }


        String pid = exitOneSubject.getId();

        //添加二级分类

        //判断二级分类是否重复

        EduSubject exitTwoSubject = this.exitTwoSubject(subjectService, subjectData.getTwoSubjectName(), pid);

        if (exitTwoSubject == null){

            exitTwoSubject = new EduSubject();

            exitTwoSubject.setParentId(pid);

            exitTwoSubject.setTitle(subjectData.getTwoSubjectName()); //一级分类名称

            subjectService.save(exitTwoSubject);

        }

    }


    //判断一级分类不能重复添加

    private EduSubject exitOneSubject(EduSubjectService subjectService, String name){

        QueryWrapper<EduSubject> wrapper = new QueryWrapper<>();

        wrapper.eq("title",name);

        wrapper.eq("parent_id","0");

        EduSubject oneSubject = subjectService.getOne(wrapper);

        return oneSubject;

    }

    //判断二级分类不能重复添加

    private EduSubject exitTwoSubject(EduSubjectService subjectService, String name,String pid){

        QueryWrapper<EduSubject> wrapper = new QueryWrapper<>();

        wrapper.eq("title",name);

        wrapper.eq("parent_id",pid);

        EduSubject twoSubject = subjectService.getOne(wrapper);

        return twoSubject;

    }


    @Override

    public void doAfterAllAnalysed(AnalysisContext analysisContext) {

    }

}




06-分类列表展示

一、前端实现

1、参考 views/tree/index.vue

2、创建api

api/edu/subject.js

import request from '@/utils/request'

export default{

    //1 课程分类

    getSubjectList(){

        return request({

            url:`/eduservice/edu-subject/getAllSubject`,

            method:'get'

        })

    }

}

3、list.vue

<template>

  <div class="app-container">

    <el-input v-model="filterText" placeholder="Filter keyword" style="margin-bottom:30px;" />

    <el-tree

      ref="tree2"

      :data="data2"

      :props="defaultProps"

      :filter-node-method="filterNode"

      class="filter-tree"

      default-expand-all

    />

  </div>

</template>

<script>

import subject from '@/api/edu/subject'

export default {

  data() {

    return {

      filterText: '',

      data2: [], //返回所有分类数据

      defaultProps: {

        children: 'children',

        label: 'title'

      }

    }

  },

  created(){

    this.getAllSubjectList()

  },

  watch: {

    filterText(val) {

      this.$refs.tree2.filter(val)

    }

  },

  methods: {

    getAllSubjectList(){

      subject.getSubjectList()

        .then(response=>{

            this.data2 = response.data.list

        })

    },

    filterNode(value, data) {

      if (!value) return true

      return data.title.toLowerCase().indexOf(value.toLowerCase()) !== -1  //优化前端过滤功能

    }

  }

}

</script>

二、后端实现

1、创建vo

//一级分类

@Data

public class OneSubject {

    private String id;

    private String title;

    //一个一级分类含有多个二级分类

    private Listchildren =new ArrayList<>();

}

//二级分类

@Data

public class TwoSubject {

    private String id;

    private String title;

}

2、创建controller

    //课程分类列表(树形)
    @ApiOperation(value = "嵌套数据列表")

    @GetMapping("getAllSubject")

    public R getAllSubject(){

        //list集合泛型是一级分类

        List<OneSubject> list = subjectService.getAllOneTwoSubject();

        return R.ok().data("list",list);

    }

3、创建service

接口

    //课程分类列表(树形)

    List<OneSubject> getAllOneTwoSubject();

实现Final

    @Override

    public List<OneSubject> getAllOneTwoSubject() {

        //1 获取一级分类 parent_id=0

        QueryWrapper<EduSubject> wrapperOne = new QueryWrapper<>();

        wrapperOne.eq("parent_id","0");

        List<EduSubject> oneSubjectList = baseMapper.selectList(wrapperOne);

        //2 获取二级分类

        QueryWrapper<EduSubject> wrapperTwo = new QueryWrapper<>();

        wrapperTwo.ne("parent_id","0");

        List<EduSubject> twoSubjectList = baseMapper.selectList(wrapperTwo);


        //创建list集合用于存储最终封装数据

        List<OneSubject> finalSubjectList = new ArrayList<>();


        //3 封装一级分类

        //查询出来的所有一级分类list集合遍历,得到每一个分类对象,获取每一个一级分类对象值

        //封装到要求的list集合里面 List<OneSubject> finalSubjectList

        for (int i = 0; i < oneSubjectList.size(); i++) {

            //得到oneSubjectList每个eduSubject对象

            EduSubject eduSubject = oneSubjectList.get(i);


            //把eduSubject里面获取出来的值,放到oneSubject对象里面

            OneSubject oneSubject = new OneSubject();

//            oneSubject.setId(eduSubject.getId());

//            oneSubject.setTitle(eduSubject.getTitle());

            BeanUtils.copyProperties(eduSubject,oneSubject);

            //多个oneSubject放到finalSubjectList里面

            finalSubjectList.add(oneSubject);


            //4 封装二级分类

            //在一级分类循环遍历查询所有二级分类

            //创建list集合封装每一个一级分类的二级分类

            List<TwoSubject> twoFinalSubjectList = new ArrayList<>();


            for (int m = 0; m < twoSubjectList.size(); m++) {

                //获取每个二级分类

                EduSubject tSubject = twoSubjectList.get(m);


                //判断二级分类parentid和一级分类id是否一样

                if (tSubject.getParentId().equals(eduSubject.getId())){

                    //把tSubject值复制到twoSubject里面,放到twoFinalSubjectList里面

                    TwoSubject twoSubject = new TwoSubject();

                    BeanUtils.copyProperties(tSubject,twoSubject);

                    twoFinalSubjectList.add(twoSubject);

                }

            }

            //把一级下面所有的二级分类放到一级分类里面

            oneSubject.setChildren(twoFinalSubjectList);

        }

        return finalSubjectList;

    }

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,242评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,769评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,484评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,133评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,007评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,080评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,496评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,190评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,464评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,549评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,330评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,205评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,567评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,889评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,160评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,475评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,650评论 2 335