php如何将文件服务器中的文件链接到数据库中的该信息

问题描述:

我是PHP的新手,我正在尝试将文件上传到文件服务器并将文件信息上传到mysql数据库,我已经完成了文件服务器和数据库部分的上传,但是我需要从我的文件中检索特定文件的信息服务器文件夹,如果我单击该文件,则尝试获取该逻辑.如果对此有任何可靠的解决方案,请帮助我. (如果我错了,请纠正我,我的想法是将文件路径和信息一起上载到数据库,这会给我解决方案吗?但是文件名可以重复)

i'm new to PHP, and i'm trying to upload file to file server and file information to mysql database, i have done uploading file server and database part but i need to retrieve the info of specific file from my file server folder if i click that file, i'm trying get that logic. please help me if there is any solid solution for this. (correct me if i'm wrong, my idea was to upload the file path to database along with info, is this will give me solution? but the filename can be duplicate)

我认为我会写一个简短的(对我来说这是简短的)答案",以便总结我的观点.

I figured I would write a short(for me this is short) "answer" just so I could summarize my points.

创建文件存储系统时,一些最佳实践".文件存储是一个广泛的类别,因此对于其中一些文件,您的里程可能会有所不同.把它们当作对我发现的效果很好的建议.

Some "Best Practices" when creating a file storage system. File storage is a broad category so your mileage may vary for some of these. Take them just as suggestion of what I found works well.

文件名 不要使用最终用户指定的名称存储文件.他们可以并且将使用各种使人痛苦的残酷角色.有些可能与'单引号一样糟糕,在Linux上基本上使单引号使它成为不可能,甚至无法(直接)删除文件.有些事情看起来很简单,例如一个空格,但是根据使用位置和服务器上的OS的不同,可能会出现one%20two.txtone+two.txtone two.txt的情况,这可能会或可能不会引起各种问题在您的链接中.

Filenames Don't store the file with the name give it by an end user. They can and will use all kind of crappy characters that will make your life miserable. Some can be as bad as ' single quotes, which on linux basically makes it so it's impossible to read, or even delete the file ( directly ). Some things can seem simple like a space but depending on where you use it and the OS on your server you could wind up with one%20two.txt or one+two.txt or one two.txt which may or may not create all kinds of issues in your links.

最好的做法是创建一个哈希,就像sha1这样,可以像{user_id}{orgianl_name}一样简单.用户名可以减少与其他用户文件名冲突的可能性.

The best thing to do is create a hash, something like sha1 this can be as simple as {user_id}{orgianl_name} The username make it less likely of collisions with other users filenames.

我更喜欢用file_hash('sha1', $contents)这样的方式,如果某人上载的文件更多,那么一旦您可以捕获到该文件(内容是相同的,则哈希是相同的).但是,如果您希望拥有较大的文件,则可能需要对其进行一些基准测试,以查看其具有哪种类型的性能.我主要处理小文件,因此可以正常工作. -请注意,带有时间戳的文件仍可以保存,因为全名不同,但是它很容易看到,并且可以在数据库中进行验证.

I prefer doing file_hash('sha1', $contents) that way if someone uploads the same file more then once you can catch that ( the contents are the same the hash is the same). But if you expect to have large files you may want to do some bench marking on it to see what type of performance it has. I mostly handle small files so it works fine for that. -note- that with the timestamp the file can still be saved because the full name is different, but it makes it quite easy to see, and it can be verified in the database.

无论您做什么,我都给它加一个时间戳记time().'-'.$filename.这是有用的信息,因为它是文件创建的绝对时间.

Regardless of what you do I would prefix it with a timestamp time().'-'.$filename. This is useful information to have, because its the absolute time the file was created.

用户提供文件的名称.只需将其存储在数据库记录中即可.这样,您可以向他们显示他们期望的名称,但是使用您知道的名称对链接始终是安全的.

As for the name a user give the file. Just store that in the database record. This way you can show them the name they expect, but use a name you know is always safe for links.

$ filename ='some crapy ^ fileane.jpg';

$filename = 'some crapy^ fileane.jpg';

$ext = strrchr($filename, '.');

echo "\nExt: {$ext}\n";

$hash = sha1('some crapy^ fileane.jpg');

echo "Hash: {$hash}\n";

$time = time();

echo "Timestamp: {$time}\n";

$hashname = $time.'-'.$hash.$ext;

echo "Hashname: $hashname\n";

食物

Ext: .jpg
Hash: bb9d2c2c7c73bb8248537a701870e35742b41c02
Timestamp: 1511853063
Hashname: 1511853063-bb9d2c2c7c73bb8248537a701870e35742b41c02.jpg

您可以在此处

路径从不存储文件的完整路径.您在数据库中所需要做的就是创建散列名称后的散列.文件所在文件夹的根"路径应在PHP中完成.这有几个好处.

Paths never store the full path to the file. All you need in the database is the hash from creating the hashed name. The "root" path to the folder the file is stored in should be done in PHP. This has several benefits.

  • 防止目录传输.因为您没有通过路径的任何部分,所以不必担心有人在其中滑过\..\..并去他们不应该去的地方.一个糟糕的例子是有人通过上传名为横向目录的文件来覆盖.htpassword文件.
  • 具有更加统一的外观链接,统一的尺寸,统一的 字符.
  • prevents directory transferal. Because your not passing any part of the path around you don't have to worry as much about someone slipping a \..\.. in there and going places they shouldn't. A poor example of this would be someone overwriting a .htpassword file by uploading a file named that with directory transverse in it.
  • Has more uniform looking links, uniform size, uniform set of characters.

https://en.wikipedia.org/wiki/Directory_traversal_attack

  • 维护.路径改变,服务器改变.您对系统进行更改的需求.如果您需要重定位这些文件,但是将它们的绝对完整路径存储在数据库中,则可以将所有内容与symlinks粘合在一起或更新所有记录.
  • Maintenance. Paths change, Servers change. Demands on your system change. If you need to relocate those files, but you stored the absolute full path to them in the DB your stuck gluing everything together with symlinks or updating all your records.

对此有一些例外.如果要将它们存储在每月文件夹中或按用户名存储.您可以将路径的该部分保存在单独的字段中.但是即使在那种情况下,您也可以基于记录中保存的数据动态地构建它.我发现最好保存尽可能少的路径信息.它们会构成一个配置或一个常量,您可以在将路径放置到文件所需的所有位置中使用.

There are some exceptions to this. If you want to store them in a monthly folder or by username. You could save that part of the path, in a seperate field. But even in that case, you could build it dynamically based on data saved in the record. I have found it's best to save as little path info as possible. And them make a config or a constant you can use in all the places you need to put the path to the file.

pathlink也非常不同,因此仅保存名称,就可以从任何PHP页面链接它,而不必从路径中减去数据.我总是发现添加到文件名然后从路径中减去会更容易.

Also the path and the link are very different, so by saving only the name you can link it from whatever PHP page you want without having to subtract data from the path. I've always found it easier to add to the filename then to subtract from a path.

数据库(只是一些建议,用法可能有所不同) 与数据一样,问自己,谁,什么,哪里,什么时候

Database (just some suggestions, use may vary ) As always with data ask yourself, who, what, where, when

  • id -int主键自动递增
  • user_id -int外键,上传了
  • 哈希-char[40] *sha1*, unique 什么哈希
  • 哈希名称-varchar {timestampl}-{hash}.{ext} 其中硬盘驱动器上的文件名
  • 文件名-varchar用户提供的原始名称,这样我们就可以向他们显示他们期望的名称(如果重要的话)
  • 状态-enum[public,private,deleted,pending.. etc]文件的状态,取决于您的用例,您可能必须查看文件,或者某些文件是私有的,只有用户可以看到它们,或者某些文件是公开的等
  • 状态日期-状态更改的时间timestamp|datetime.
  • 创建日期-timestamp|datetime 创建时间,首选时间戳记是因为它使某些事情变得更容易,但在哈希名称中应使用相同的时间戳记,在这种情况下.
  • 类型-varchar-MIME类型,可用于在下载等时设置MIME类型.
  • id - int primary key auto increment
  • user_id - int foreign key, who uploaded it
  • hash - char[40] *sha1*, unique what the hash
  • hashname - varchar {timestampl}-{hash}.{ext} where the files name on the hard drive
  • filename - varchar the original name give by the user, that way we can show them the name they expect ( if that is important )
  • status - enum[public,private,deleted,pending.. etc] status of the file, depending on your use case, you may have to review the files, or maybe some are private only the user can see them, maybe some are public etc.
  • status_date - timestamp|datetime time the status was changed.
  • create_date - timestamp|datetime when time the file was created, a timestamp is prefered as it makes some things easier but it should be the same timestamp use in the hashname, in that case.
  • type - varchar - mime type, can be useful for setting the mime type when downloading etc.

如果希望不同的用户上传相同的文件,并且使用file_hash,则可以使hash字段成为user_idhash的组合唯一索引,这样仅当同一位用户上传了同一文件.您还可以根据需要根据时间戳和哈希值进行操作.

If you expect different users to upload the same file and you use the file_hash you can make the hash field a combined unique index of the user_id and the hash this way it would only conflict if the same user uploaded the same file. You could also do it based on the timestamp and hash, depending on your needs.

这是我能想到的基本内容,这并不是绝对的,只是我认为有用的某些领域.

That's the basic stuff I could think of, this isn't an absolute just some fields I thought would be useful.

单独拥有哈希值很有用,如果您自己存储哈希值,则可以将其存储在sha1的CHAR(40)中(占用的数据库空间少于VARCHAR)并将排序规则设置为UTF8_bin是二进制的.这使得对它的搜索区分大小写.尽管散列冲突的可能性很小,但这会增加一点保护,因为散列是大写的小写字母.

It's useful to have the hash by itself, if you store it by it's self you can store it in a CHAR(40) for sha1 (takes up less space in the DB then VARCHAR) and set the collation, to UTF8_bin which is binary. This makes searches on it case sensitive. Although there is little possibility of a hash collision, this adds just a bit more protection because hashes are upper an lower case letters.

如果您存储扩展名,并且时间戳是分开的,则始终可以随时动态构建hashname.如果您发现自己一次又一次地创建事物,则可能只想将其存储在数据库中以简化PHP中的工作.

You can always build the hashname on the fly if you store the extension, and the timestamp separate. If you find yourself creating things time and time again you may just want to store it in the DB to simplify the work in PHP.

我只喜欢将哈希放在链接中,没有扩展名,所以我的链接看起来像这样.

I like just putting the hash in the link, no extension no anything so my links look like this.

http://www.example.com/download/ad87109bfff0765f4dd8cf4943b04d16a4070fea

真正简单,真正的泛型,URL中的安全大小始终相同,等等.

Real simple, real generic, safe in urls always the same size etc..

此文件"的hashname就像这样

1511848005-ad87109bfff0765f4dd8cf4943b04d16a4070fea.jpg

如果您确实与同一个文件和不同的用户(我在上面提到)有冲突.您始终可以将时间戳部分添加到链接中,或者将user_id添加到链接中,也可以将两者都添加到链接中.如果您使用user_id,将其用零填充会很有用.例如,某些用户可能具有ID:1,而某些用户可能是ID:234,因此您可以将其保留到4个位置,并将其设置为00010234.然后将其添加到哈希中,这几乎是不明显的:

If you do have conflicts with the same file and different user(which I mentioned above). You can always add the timestamp part into the link, the user_id or both. If you use the user_id, it might be useful to left pad it with zeros. For example some users may have ID:1 and some may be ID:234 so you could left pad it to 4 places and make them 0001 and 0234. Then add that to the hash, which is almost unnoticeable:

1511848005-ad87109bfff0765f4dd8cf4943b04d16a4070fea0234.jpg

这里重要的是,因为sha1始终为40,并且id始终为4,所以我们可以准确而轻松地将二者分开.这样,您仍然可以唯一地查找它.有很多不同的选择,但是很大程度上取决于您的需求.

The important thing here is that because sha1 is always 40 and the id is always 4 we can separate the two accurately and easily. And this way, you can still look it up uniquely. There are a lot of different options but so much depends on your needs.

访问权限 如下载.您应该始终使用PHP输出文件,不要让他们直接访问该文件.最好的方法是将文件存储在webroot之外(public_htmlwww文件夹上方).然后在PHP中,您可以将标头设置为正确的类型,并且基本上可以读出文件.除视频外,这几乎适用于所有其他功能.我不处理视频,所以这不是我的经验范围.但是我发现最好将其视为所有文件数据都是文本,其标题会将文本变成图像,或者是excel文件或pdf.

Access Such as downloading. You should always output the file with PHP, don't give them direct access to the file. The best way is to store the files outside of the webroot ( above the public_html, or www folder ). Then in PHP you can set the headers to the correct type ans basically read out the file. This works for pretty much everything except video. I don't handle videos so that's a topic outside of my experience. But I find it best to think of it as all file data is text, its the headers that make that text into an image, or an excel file or a pdf.

不给他们直接访问文件的最大好处是,如果您有会员网站,或者不想在没有登录的情况下访问您的内容,则可以轻松地检查PHP是否已登录,然后再给他们提供文件.内容.而且,由于该文件位于webroot外部,因此他们无法以其他任何方式访问它.

The big advantage of not giving them direct access to the file is if you have a membership site, of don't want your content accessible without a login, you can easily check in PHP if they are logged in before giving them the content. And, as the file is outside the webroot, they can't access it any other way.

最重要的是选择一致的东西,它仍然足够灵活,可以满足您的所有需求.

The most important thing is to pick something consistent, that is still flexible enough to handle all your needs.

我确定我可以提出更多建议,但是如果您有任何建议,请随时发表评论.

I'm sure I can come up with more, but if you have any suggest feel free to comment.

基本过程流

  1. 用户提交表单(enctype="multipart/form-data")

https://www.w3schools.com/tags/att_form_enctype.asp

  1. 服务器从Super Globals $_POST$_FILES
  2. 表单接收帖子
  1. Server receives the post from the form, Super Globals $_POST and the $_FILES

http://php.net/manual/zh/reserved.variables .files.php

$_FILES = [
 'fieldname' => [
        'name' => "MyFile.txt" // (comes from the browser, so treat as tainted)
        'type' => "text/plain" //  (not sure where it gets this from - assume the browser, so treat as tainted)
        'tmp_name' => "/tmp/php/php1h4j1o" // (could be anywhere on your system, depending on your config settings, but the user has no control, so this isn't tainted)
        'error' => "0" //UPLOAD_ERR_OK  (= 0)
        'size' => "123" //   (the size in bytes)
    ]
 ];

  1. 检查错误if(!$_FILES['fielname']['error'])

清除显示名称$filename = htmlentities($str, ENT_NOQUOTES, "UTF-8");

保存文件,创建数据库记录(PSUDO-CODE)

Save file, create DB record ( PSUDO-CODE )

赞:

 $path = __DIR__.'/uploads/'; //for exmaple

$time = time();
$hash = hash_file('sha1',$_FILES['fielname']['tmp_name']);
$type = $_FILES['fielname']['type'];
$hashname = $time.'-'.$hash.strrchr($_FILES['fielname']['name'], '.');
$status = 'pending';

if(!move_uploaded_file ($_FILES['fielname']['tmp_name'], $path.$hashname  )){
     //failed
     //do somehing for errors.
     die();
}


//store record in db

http://php.net/manual/zh/function .move-uploaded-file.php

  1. 创建链接(因路由而异),简单的方法是像这样http://www.example.com/download?file={$hash}进行链接,但http://www.example.com/download/{$hash}

用户点击链接会转到下载页面.

user clicks link goes to download page.

获取输入并查找记录

$hash = $_GET['file'];

$stmt = $PDO->prepare("SELECT * FROM attachments WHERE hash = :hash LIMIT 1");  
$stmt->execute([":hash" => $hash]);

$row = $stmt->fetch(PDO::FETCH_ASSOC);

print_r($row);

http://php.net/manual/en/intro.pdo.php

等等...

干杯!