静态文件与上传

来自我的博客

在日常的后端开发中, 不可避免的就是要处理各种文件上传的需要, 随之而来的就是各种下载的需求, 我遇到的大部分开发者都会考虑到在上传的时候做简单的文件校验(有的也没有做), 然而在文件下载的问题上, 则采用依赖于web服务器的静态文件传输, 经常造成很多的麻烦。

上传

大部分开发者实际上在上传的时候都会对文件进行校验, 并且为了安全性考虑甚至会将文件更换名称。但是更换名称后简单的依赖于web服务器进行下载则会让用户拥有一种我的文件被篡改过的感觉(文件名发生了变化)。

依赖于web服务器的文件上传,则可以认为是相对依赖于浏览器的url的一种操作,一旦我们将web应用部署在二级目录下,如:

https://origin/path/

则很容易出现存储问题,比如我们存储的目录很可能因为这个原因需要多嵌套一层path目录

下载

下载遇到的问题同理,最经常遇到的则是静态文件仅仅通过web服务器来做下载,不方便做权限管理,并且容易发生盗用资源的现象,其他的web应用用着你的资源,但是却耗费着你的带宽。

另一个则和上传的问题一样,下载的时候也避免不了遇到二级目录的问题,这时候web服务器很容易就带着二级目录去做寻找。

并且在资源进行删除的时候,其相关的静态文件如果没有进行处理也不好做清理,容易造成垃圾文件堆叠。

解决

我个人在这方面也是有吃过很多的亏,上面的问题全部都碰到过,最后决定使用各种编程语言中读取文件的api来进行解决:

  • 文件名更换
  • 二级目录的问题
  • 权限
  • 下载

其中文件名更换非常的简单,我习惯于在文件进行存储时,对其旧的文件名进行持久化存储,以PHP为例:

<?php

// DATA_DIR 为web应用根目录的相对路径常量

$uniqid = uniqid();
$path = DATA_DIR . "/attached/{$uniqid}";

$ext = pathinfo($file['name'], PATHINFO_EXTENSION);
$name = date("YmdHis") . '_' . rand(10000, 99999) . '.' . $ext;

if (is_dir($path) && is_uploaded_file($file['tmp_name'])
&& move_uploaded_file($file['tmp_name'], "{$path}/{$name}")) {
    $old = $file['name'];
    $dir = $path;
    $path = "{$path}/{$name}";
    $mime = $file['type'];
}

我们可以获得文件相关的四个变量,其中$old则为我们解决了文件名更换的问题,我们会将用户文件的文件名进行存储,而在我们本地则使用了$name来重命名保证安全文件

同时,二级目录问题也得到了解决,这里使用的是我们自定义的目录$path,不会受到部署的影响。

在下载的时候,我们也可以依赖于编程语言提供的api:

<?php
// APP_PATH 为web应用根目录的绝对路径常量

$article; // 假设我们的数据库映射对象是一篇文章
if (!$article->id) $this->redirect('error/404');
$fullpath = APP_PATH . '/' . $article->path;

header("Content-Type: {$article->mime}");
header('Accept-Ranges: bytes');
header('Accept-Length:' . filesize($fullpath));
header("Content-Disposition: attachment; filename=\"{$article->old}\"");
ob_clean();
echo file_get_contents($fullpath);
exit;

这里由于是编程语言的实现,那么我们依赖于权限的问题也得到了解决,如第5行中代码返回404错误一样,我们也可以进行逻辑判断并返回401错误

并且同时,这里的下载也会从我们的数据库里面寻找文件,不用再被web服务器的二级目录所困扰,并且如果更加深入一下,额外的代码也可以降低我们的资源被盗用的风险。

以下是node的http框架koa的实现:

const router = require('koa-router')()
const send = require('koa-send');

router.get('/image', async ctx => {
  const mongo = new Database()
  const db = await mongo.init()
  let platform = await db.collection('platform').findOne({ "_id": "10010" })
  if (platform) {
    ctx.attachment(platform.fileName);
    await send(ctx, platform.fileName, { root: `${__dirname}/../${platform.filePath}` });
  }
  else {
    ctx.body = {
      data: {},
      code: 400,
      message: '获取失败'
    }
  }
  db.close()
})

也很简单方便

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值