vue+elementUI实现文件上传与下载

需要到达的效果

文件上传页面
1、点击上传显示弹窗,并成功添加文件,且支持多文件多格式上传
选择文件
2、点击确定按钮,文件成功保存至本地目录,并且将文件信息保存至数据库
  • 由于我们并没有设置表单内容也没有做表单规则,点击下载文件可展开文件列表
    信息列表
3、点击下载文件,文件能下载至本地
下载文件

文件保存窗口

文件上传代码代码实现

1、前端,由于我们使用的是elementUI,下面我们贴上代码
  • 上传参数设置
// 引入方法
import {
        listTreatment,
        getTreatment,
        delTreatment,
        addTreatment,
        updateTreatment,
        exportTreatment
    } from "@/api/hospital/treatment";
// 上传参数
  data() {
            return {      
                // 非多个禁用
                multiple: true,  
                // 文件下载列表                             
                dataFileList: [],
                // 表单参数
                form: {},
                // 表单校验
                rules: {},
                // 上传参数
                upload: {
                    // 是否禁用上传
                    isUploading: false,
                    // 设置上传的请求头部
                    headers: {Authorization: "Bearer " + getToken()},
                    // 上传的地址
                    url: process.env.VUE_APP_BASE_API + "/system/treatment/upload",
                    // 上传的文件列表
                    fileList: []
                },
            };
        },
  • elementUI代码
<el-dialog :title="title" :visible.sync="open" width="900px" append-to-body>
  <el-form ref="form" :model="form" :rules="rules" label-width="80px">
      <el-form-item label="请选择方案" label-width="100px">
          <el-upload
            ref="upload"
            action=""
            :on-remove="handleRemove"
            :on-change="fileChange"
            :before-upload="beforeAvatarUpload"
            :before-remove="beforeRemove"
            :auto-upload="false"
            multiple
            :file-list="upload.fileList">
            <el-button size="small" type="primary">点击上传</el-button>
            <!-- <div slot="tip" class="el-upload__tip">只能上传jpg/png文件,且不超过500kb</div>-->
          </el-upload>
      </el-form-item>
  </el-form>
  <div slot="footer" class="dialog-footer">
        <el-button type="primary" @click="submitForm">确 定</el-button>
        <el-button @click="cancel">取 消</el-button>
  </div>
</el-dialog>
  • js代码,由于我们对都是对文件列表upload.fileList参数进行操作
            /** 文件预上传格式限制 */
            beforeAvatarUpload(file) {
                // const isJPG = file.type === 'image/jpeg'
                // const isPng = file.type === 'image/png'
                // const isLt2M = file.size / 1024 / 1024 < 2
                //
                // if (!isJPG && !isPng) {
                //     this.$message.error('上传图片只能是 JPG或png 格式!')
                // }
                // if (!isLt2M) {
                //     this.$message.error('上传图片大小不能超过 2MB!')
                // }
                // return (isJPG || isPng) && isLt2M
            },
            /** 文件移除时 */
            handleRemove(file, fileList) {
                for (const i in this.picList) {
                    if (this.picList[i].key === file.uid) {
                        this.picList.splice(i, 1)
                    }
                }
                this.upload.fileList = fileList
            },
            // 文件改变时
            fileChange(file, fileList) {
                this.upload.fileList = fileList
            },
            // 文件移除之前
            beforeRemove(file, fileList) {
                return this.$confirm(`确定移除 ${file.name}?`);
            },
            /** 提交按钮 */
            submitForm() {
                this.$refs["form"].validate(valid => {
                    if (valid) {
                        if (this.form.tId != null) {
                            let formData = new FormData()
                            this.upload.fileList.forEach(file => {
                                formData.append('file', file.raw)
                            });
                            formData.append("tId", this.form.tId)
                            formData.append("snakebiteType", this.form.snakebiteType)
                            formData.append("programmeName", this.form.programmeName)
                            formData.append("programmeDescribe", this.form.programmeDescribe)
                            formData.append("orgName", this.form.orgName)
                            this.bingOrgCode(this.form.orgName)
                            formData.append("orgCode", this.form.orgCode)
                            updateTreatment(formData).then(response => {
                                this.msgSuccess("修改成功");
                                this.open = false;
                                this.getList();
                            });
                        } else {
                            let formData = new FormData()
                            this.upload.fileList.forEach(file => {
                                formData.append('file', file.raw)
                            })
                            formData.append("snakebiteType", this.form.snakebiteType)
                            formData.append("programmeName", this.form.programmeName)
                            formData.append("programmeDescribe", this.form.programmeDescribe)
                            formData.append("orgCode", this.form.orgCode)
                            formData.append("orgName", this.form.orgName)
                            addTreatment(formData).then(response => {
                                this.msgSuccess("新增成功");
                                this.open = false;
                                this.getList();
                            });
                        }
                    }
                });
            },
          // 取消按钮
            cancel() {
                this.open = false;
                this.reset();
            },
  • JS文件设置跳转
/ 新增蛇伤治疗
export function addTreatment(data) {
  return request({
    url: '/hospital/treatment',
    method: 'post',
    data: data
  })
}
2、后端,我们使用的springboot,下面我们贴上代码
  • controller 控制层接收参数,这里给出实现类,接口也是一样的
@RestController
@RequestMapping("/hospital/treatment")
public class SnakebiteTreatmentController extends BaseController
{
    @Autowired
    private ISnakebiteTreatmentService snakebiteTreatmentService;

    /**
     * 新增蛇伤治疗,并上传文件
     */
    //@PreAuthorize("@ss.hasPermi('hospital:treatment:add')")
    @Log(title = "蛇伤治疗", businessType = BusinessType.INSERT)
    @PostMapping
    public AjaxResult add(@RequestParam(value = "file") MultipartFile[] file,SnakebiteTreatment snakebiteTreatment) throws Exception
    {
        return toAjax(snakebiteTreatmentService.insertSnakebiteTreatment(file, snakebiteTreatment));
    }

}
  • service 业务层处理
这里使用 MultipartFile[] file 来存储多个文件,首先对MultipartFile的 file对象进行遍历查看是否有多个文件,使用for (MultipartFile newFile : file)生成newFile 对象
  • 使用MultipartFile里的getOriginalFilename()方法来获取当前newFile的文件名称
    String fileName = newFile.getOriginalFilename();
  • 使用FileUploadUtils文件工具类里的getExtension(newFile)来获取文件类型
    String fileType = FileUploadUtils.getExtension(newFile);
  • 设置文件路径前缀,可以手动写死设置。我这里使用的是若依
    String filePrefix = RuoYiConfig.getProfile();
  • 使用FileUploadUtils文件工具类里的upload(前缀,文件对象)进行上传文件操作,并返回文件后缀
  • 使用字符串分隔符进行分割后缀
    String savePath = fileSuffix.substring(9);
@Service
public class SnakebiteTreatmentServiceImpl implements ISnakebiteTreatmentService {
    @Autowired
    private SnakebiteTreatmentMapper snakebiteTreatmentMapper;
    @Autowired
    private SnakebiteTreatmentFileServiceImpl snakebiteTreatmentFileService;

    /**
     * 新增蛇伤治疗,并上传文件
     *
     * @param snakebiteTreatment 蛇伤治疗
     * @return 结果
     */
    @Override
    public int insertSnakebiteTreatment(MultipartFile[] file, SnakebiteTreatment snakebiteTreatment) throws Exception {
        int code = snakebiteTreatmentMapper.insertSnakebiteTreatment(snakebiteTreatment);
        Long tId = snakebiteTreatment.gettId();
        //有上传文件就处理,没有就不处理
        if (file != null && file.length > 0) {
            if (tId != null) {
                ArrayList<SnakebiteTreatmentFile> list = new ArrayList<>();
                for (MultipartFile newFile : file) {
                    if (newFile.isEmpty()) {
                        return -1;
                    }
                    // 获取文件名称
                    String fileName = newFile.getOriginalFilename();
                    // 获取文件类型
                    String fileType = FileUploadUtils.getExtension(newFile);
                    // 获取文件路径前缀
                    String filePrefix = RuoYiConfig.getProfile();
                    // 获取文件路径后缀
                    String fileSuffix = FileUploadUtils.upload(filePrefix, newFile);
                    // 设置文件保存路径
                    String savePath = fileSuffix.substring(9);
                    // 将主表自增主键、文件名,文件路径,文件类型保存至文件表数据库中
                    SnakebiteTreatmentFile treatmentFile = new SnakebiteTreatmentFile();
                    treatmentFile.settId(tId);
                    treatmentFile.settFileType(fileType);
                    treatmentFile.settFileName(fileName);
                    treatmentFile.settFilePath(savePath);
                    list.add(treatmentFile);
                }
                snakebiteTreatmentFileService.insertSnakebiteTreatmentFile(list);
            }
        }
        return code;
    }

}
  • dao持久层处理
public int insertSnakebiteTreatmentFile(List<SnakebiteTreatmentFile> snakebiteTreatmentFile);
  • 我们这里使用的是mybatis,下面贴上操作文件数据库表代码
    <insert id="insertSnakebiteTreatmentFile" parameterType="SnakebiteTreatmentFile" useGeneratedKeys="true" keyProperty="tfId">
        insert into snakebite_treatment_file (t_id,t_file_name,t_file_path,t_file_type) values
        <foreach collection="list" item="item" index="index" separator=",">
            (#{item.tId}, #{item.tFileName}, #{item.tFilePath},#{item.tFileType})
        </foreach>
    </insert>
到这里文件上传已经完成,下面贴上FileUploadUtils文件上传工具类代码
package com.zyy.common.utils.file;

import java.io.File;
import java.io.IOException;

import org.apache.commons.io.FilenameUtils;
import org.springframework.web.multipart.MultipartFile;
import com.zyy.common.config.RuoYiConfig;
import com.zyy.common.constant.Constants;
import com.zyy.common.exception.file.FileNameLengthLimitExceededException;
import com.zyy.common.exception.file.FileSizeLimitExceededException;
import com.zyy.common.exception.file.InvalidExtensionException;
import com.zyy.common.utils.DateUtils;
import com.zyy.common.utils.StringUtils;
import com.zyy.common.utils.uuid.IdUtils;

/**
 * 文件上传工具类
 *
 * @author zyy
 */
public class FileUploadUtils {
    /**
     * 默认大小 50M
     */
    public static final long DEFAULT_MAX_SIZE = 50 * 1024 * 1024;

    /**
     * 默认的文件名最大长度 100
     */
    public static final int DEFAULT_FILE_NAME_LENGTH = 100;

    /**
     * 默认上传的地址
     */
    private static String defaultBaseDir = RuoYiConfig.getProfile();

    public static void setDefaultBaseDir(String defaultBaseDir) {
        FileUploadUtils.defaultBaseDir = defaultBaseDir;
    }

    public static String getDefaultBaseDir() {
        return defaultBaseDir;
    }

    /**
     * 以默认配置进行文件上传
     *
     * @param file 上传的文件
     * @return 文件名称
     * @throws Exception
     */
    public static final String upload(MultipartFile file) throws IOException {
        try {
            return upload(getDefaultBaseDir(), file, MimeTypeUtils.DEFAULT_ALLOWED_EXTENSION);
        } catch (Exception e) {
            throw new IOException(e.getMessage(), e);
        }
    }

    /**
     * 根据文件路径上传
     *
     * @param baseDir 相对应用的基目录
     * @param file    上传的文件
     * @return 文件名称
     * @throws IOException
     */
    public static final String upload(String baseDir, MultipartFile file) throws IOException {
        try {
            return upload(baseDir, file, MimeTypeUtils.DEFAULT_ALLOWED_EXTENSION);
        } catch (Exception e) {
            throw new IOException(e.getMessage(), e);
        }
    }

    /**
     * 文件上传
     *
     * @param baseDir          相对应用的基目录
     * @param file             上传的文件
     * @param allowedExtension 上传文件类型
     * @return 返回上传成功的文件名
     * @throws FileSizeLimitExceededException       如果超出最大大小
     * @throws FileNameLengthLimitExceededException 文件名太长
     * @throws IOException                          比如读写文件出错时
     * @throws InvalidExtensionException            文件校验异常
     */
    public static final String upload(String baseDir, MultipartFile file, String[] allowedExtension)
            throws FileSizeLimitExceededException, IOException, FileNameLengthLimitExceededException,
            InvalidExtensionException {
        int fileNamelength = file.getOriginalFilename().length();
        if (fileNamelength > FileUploadUtils.DEFAULT_FILE_NAME_LENGTH) {
            throw new FileNameLengthLimitExceededException(FileUploadUtils.DEFAULT_FILE_NAME_LENGTH);
        }

        assertAllowed(file, allowedExtension);

        String fileName = extractFilename(file);

        File desc = getAbsoluteFile(baseDir, fileName);
        file.transferTo(desc);
        String pathFileName = getPathFileName(baseDir, fileName);
        return pathFileName;
    }

    /**
     * 编码文件名
     */
    public static final String extractFilename(MultipartFile file) {
        String fileName = file.getOriginalFilename();
        String extension = getExtension(file);
//        fileName = DateUtils.datePath() + "/" + IdUtils.fastUUID() + "." + extension;
        fileName = DateUtils.datePath() + "/" + fileName;
        return fileName;
    }

    private static final File getAbsoluteFile(String uploadDir, String fileName) throws IOException {
        File desc = new File(uploadDir + File.separator + fileName);

        if (!desc.getParentFile().exists()) {
            desc.getParentFile().mkdirs();
        }
        if (!desc.exists()) {
            desc.createNewFile();
        }
        return desc;
    }

    private static final String getPathFileName(String uploadDir, String fileName) throws IOException {
        int dirLastIndex = RuoYiConfig.getProfile().length() + 1;
        String currentDir = StringUtils.substring(uploadDir, dirLastIndex);
        String pathFileName = Constants.RESOURCE_PREFIX + "/" + currentDir + "/" + fileName;
        return pathFileName;
    }

    /**
     * 文件大小校验
     *
     * @param file 上传的文件
     * @return
     * @throws FileSizeLimitExceededException 如果超出最大大小
     * @throws InvalidExtensionException
     */
    public static final void assertAllowed(MultipartFile file, String[] allowedExtension)
            throws FileSizeLimitExceededException, InvalidExtensionException {
        long size = file.getSize();
        if (DEFAULT_MAX_SIZE != -1 && size > DEFAULT_MAX_SIZE) {
            throw new FileSizeLimitExceededException(DEFAULT_MAX_SIZE / 1024 / 1024);
        }

        String fileName = file.getOriginalFilename();
        String extension = getExtension(file);
        if (allowedExtension != null && !isAllowedExtension(extension, allowedExtension)) {
            if (allowedExtension == MimeTypeUtils.IMAGE_EXTENSION) {
                throw new InvalidExtensionException.InvalidImageExtensionException(allowedExtension, extension,
                        fileName);
            } else if (allowedExtension == MimeTypeUtils.FLASH_EXTENSION) {
                throw new InvalidExtensionException.InvalidFlashExtensionException(allowedExtension, extension,
                        fileName);
            } else if (allowedExtension == MimeTypeUtils.MEDIA_EXTENSION) {
                throw new InvalidExtensionException.InvalidMediaExtensionException(allowedExtension, extension,
                        fileName);
            } else {
                throw new InvalidExtensionException(allowedExtension, extension, fileName);
            }
        }

    }

    /**
     * 判断MIME类型是否是允许的MIME类型
     *
     * @param extension
     * @param allowedExtension
     * @return
     */
    public static final boolean isAllowedExtension(String extension, String[] allowedExtension) {
        for (String str : allowedExtension) {
            if (str.equalsIgnoreCase(extension)) {
                return true;
            }
        }
        return false;
    }

    /**
     * 获取文件名的后缀
     *
     * @param file 表单文件
     * @return 后缀名
     */
    public static final String getExtension(MultipartFile file) {
        String extension = FilenameUtils.getExtension(file.getOriginalFilename());
        if (StringUtils.isEmpty(extension)) {
            extension = MimeTypeUtils.getExtension(file.getContentType());
        }
        return extension;
    }
}

-------------------------------------------------------------------------------------------

文件下载代码实现

1、前端,点击方案列表下载方案(文件)按钮
下载文件
  • j方案列表弹窗代码
<el-dialog title="方案列表" :visible.sync="openDownload" width="900px" append-to-body>
      <el-form ref="fileForm" label-width="80px">
        <el-table :data="dataFileList">
          <el-table-column label="序号" align="center" width="50px" type="index"/>
          <el-table-column label="方案名称" align="center" prop="tFileName"/>
          <el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="220px">
            <template slot-scope="scope">
              <el-button
                size="mini"
                type="text"
                icon="el-icon-download"
                @click="download(scope.row)"
                v-hasPermi="['hospital:treatment:download']"
              >下载方案
              </el-button>
              <el-button
                size="mini"
                type="text"
                icon="el-icon-delete"
                @click="handleFileDelete(scope.row)"
                v-hasPermi="['hospital:treatment:remove']"
              >删除方案
              </el-button>
            </template>
          </el-table-column>
        </el-table>
      </el-form>
      <div slot="footer" class="dialog-footer">
        <el-button @click="cancelDownload">取 消</el-button>
      </div>
    </el-dialog>
  • js代码
// 文件下载
            download(row) {
                // window.open(process.env.VUE_APP_BASE_API + "/hospital/file/downloadFile?tPath=" + tPath)
                var name = row.tFileName;
                var url = row.tFilePath;
                const a = document.createElement('a')
                a.setAttribute('download', name)
                a.setAttribute('target', '_blank')
                a.setAttribute('href', process.env.VUE_APP_BASE_API + "/hospital/file/downloadFile?tPath=" + url)
                a.click()
            },
1、后端
  • controller 控制层接收文件路径
  • 我们通过使用文件工具类FileUtils的downloadUtil(response, filePrefix + tPath, fileName)方法进行处理,并传入response,前缀与地址拼接,文件名
    /**
     * 下载文件
     */
    @PreAuthorize("@ss.hasPermi('hospital:treatmentFile:download')")
    @RequestMapping("/downloadFile")
    public void downloadFile(HttpServletRequest request, HttpServletResponse response, @RequestParam("tPath") String tPath) throws IOException {
        // 获取文件路径前缀
        String filePrefix = RuoYiConfig.getProfile();
        // 获取文件名
        String fileName = tPath.substring(tPath.lastIndexOf("/") + 1);
        // 文件下载
        FileUtils.downloadUtil(response, filePrefix + tPath, fileName);
    }
到这里文件下载也已经完成,下面贴上FileUtils文件工具类代码
package com.zyy.hospital.utils;


import org.springframework.web.multipart.MultipartFile;

import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletResponse;
import java.awt.image.BufferedImage;
import java.io.*;
import java.net.URLEncoder;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

/**
 * 文件工具类
 */
public class FileUtils {

    /**
     * 上传文件工具
     *
     * @param UPLOAD_FILES_PATH 上传路径
     * @param fileName          文件名称
     * @param file              文件
     */
    public static Boolean upLoadUtil(String UPLOAD_FILES_PATH, String fileName, MultipartFile file) throws IOException {
        if (file.isEmpty()) {
            return false;
        } else {
            File dest = new File(UPLOAD_FILES_PATH + "/" + fileName);
            //判断文件父目录是否存在
            if (!dest.getParentFile().exists()) {
                dest.getParentFile().mkdirs();
            }
            file.transferTo(dest);
        }
        return true;
    }


    /**
     * 下载文件工具
     *
     * @param response response对象
     * @param realPath 文件路径
     * @param fileName 文件名称
     */
    public static void downloadUtil(final HttpServletResponse response, String realPath, String fileName) throws IOException {
        File file = new File(realPath);
        if (file.exists()) {
            response.setHeader("content-type", "application/octet-stream");
            response.setContentType("application/octet-stream");
            // 下载文件能正常显示中文
            try {
                response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8"));
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            }

            // 实现文件下载
            byte[] buffer = new byte[1024];
            FileInputStream fis = null;
            BufferedInputStream bis = null;
            try {
                fis = new FileInputStream(file);
                bis = new BufferedInputStream(fis);
                OutputStream os = response.getOutputStream();
                int i = bis.read(buffer);
                while (i != -1) {
                    os.write(buffer, 0, i);
                    i = bis.read(buffer);
                }
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                assert bis != null;
                bis.close();
                fis.close();
            }
        }
    }

    /**
     * 删除文件夹
     *
     * @param sPath 文件夹路径
     */
    public static boolean DeleteFolder(String sPath) {
        boolean flag = false;
        File file = new File(sPath);
        // 判断目录或文件是否存在
        if (!file.exists()) {  // 不存在返回 false
            return flag;
        } else {
            // 判断是否为文件
            if (file.isFile()) {  // 为文件时调用删除文件方法
                return deleteFile(sPath);
            } else {  // 为目录时调用删除目录方法
                return deleteDirectory(sPath);
            }
        }
    }

    /**
     * 删除单个文件
     *
     * @param sPath 被删除文件的文件名
     * @return 单个文件删除成功返回true,否则返回false
     */
    public static boolean deleteFile(String sPath) {
        boolean flag = false;
        File file = new File(sPath);
        // 路径为文件且不为空则进行删除
        if (file.isFile() && file.exists()) {
            file.delete();
            flag = true;
        }
        return flag;
    }

    /**
     * 删除目录(文件夹)以及目录下的文件
     *
     * @param sPath 被删除目录的文件路径
     * @return 目录删除成功返回true,否则返回false
     */
    public static boolean deleteDirectory(String sPath) {
        //如果sPath不以文件分隔符结尾,自动添加文件分隔符
        if (!sPath.endsWith(File.separator)) {
            sPath = sPath + File.separator;
        }
        File dirFile = new File(sPath);
        //如果dir对应的文件不存在,或者不是一个目录,则退出
        if (!dirFile.exists() || !dirFile.isDirectory()) {
            return false;
        }
        boolean flag = true;
        //删除文件夹下的所有文件(包括子目录)
        File[] files = dirFile.listFiles();
        for (int i = 0; i < files.length; i++) {
            //删除子文件
            if (files[i].isFile()) {
                flag = deleteFile(files[i].getAbsolutePath());
                if (!flag) {
                    break;
                }
            } //删除子目录
            else {
                flag = deleteDirectory(files[i].getAbsolutePath());
                if (!flag) {
                    break;
                }
            }
        }
        if (!flag) {
            return false;
        }
        //删除当前目录
        if (dirFile.delete()) {
            return true;
        } else {
            return false;
        }
    }

    /**
     * 转文件格式
     *
     * @param img 照片文件
     */
    public static byte[] fileToByte(File img) throws Exception {
        byte[] bytes = null;
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        try {
            BufferedImage bi;
            bi = ImageIO.read(img);
            ImageIO.write(bi, "jpg", baos);
            bytes = baos.toByteArray();
        } catch (Exception e) {
            //            e.printStackTrace();
        } finally {
            // 关闭输出流
            baos.close();
        }
        return bytes;
    }
}
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,793评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,567评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,342评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,825评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,814评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,680评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,033评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,687评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,175评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,668评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,775评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,419评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,020评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,978评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,206评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,092评论 2 351
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,510评论 2 343

推荐阅读更多精彩内容