HTML5+PHP 实现 保存文件夹相对路径 递归上传 在线浏览
来源:互联网 发布:淘宝店铺客服不回话 编辑:程序博客网 时间:2024/06/14 20:24
1.选择要上传的文件夹,上传以后默认都在根目录下。
2.看看后台管理界面的效果,实现多级目录,可以显示图片内容,返回上一级
正文:
谈到文件夹上传,应该都不觉得难,一个input框加上一个PHP后台就够了。但是这次的需求说起来容易,但是其实还挺难的。要把一个文件夹的文件递归上传,保存目录结构,能够在浏览器里展示出来,其实是三个过程。
【1】上传时要保存文件的相对路径。这里主要有两个问题。第一是怎样获取到路径,第二是用什么方式传到服务器。
【2】后台接受并且构成出目录结构。这里也是两个问题。第一,目录结构要怎么从前台post来的数据中分离出来。第二,用什么样的结构去保存,用什么样的逻辑去存储。其实这就是后台的“算法”了。
【3】让前台显示文件目录。首先考虑到如果文件很多,你不能一次性从数据库中全都select出来。然后我希望代码尽量优雅,最好和后台交互一个函数就搞定。
解决方案:
最最重要,先确定开发的浏览器环境,这里选择Chrome,2016年6月以后的版本都算比较新,没有一一去试,chrome保证功能无误,且可以移动端联调。HTML5 chrome移动设备和电脑端联调
【1】
首先定义一个能够承载文件夹的标签。webkitdirectory是这里的大杀器,这个不仅定义了文件夹上传,而且记录了文件的相对路径。
- <input type="file" name="multi_upload[]" id="multi_upload" multiple webkitdirectory />
文件本身的上传,是用到了ThinkPHP的upload类,一旦你把input的files信息post给后台,只要这样几行代码就能指定目录保存了。
- $upload = new \Think\Upload();// 实例化上传类
- $upload->maxSize = 3145728 ;// 设置附件上传大小
- $upload->exts = array('jpg', 'gif', 'png', 'jpeg');// 设置附件上传类型
- $upload->rootPath = './Public/octdatabase/'; // 设置附件上传根目录
- $upload->savePath = ''; // 设置附件上传(子)目录
- $targetFolder = '/Public/octdatabase/'; // Relative to the root
- $upload->saveName = array('myFun',array('__FILE__'));
- $upload->autoSub = false;
- // 上传文件
- $info = $upload->upload();
相对路径又如何传给后台呢?相对路径没有包含在files的里面,需要单独挖出来上传。这里有一些小trick,比如files的最后两个其实不是图片文件本身,XMLHttpRequest是做了一个类似Ajax的交互,页面不会跳转,所以我把Ajax返回的内容显示在一个div标签里,这样就可以在网页中运行。 这部分代码主体参考了Alan的Blog:http://sapphion.com/2012/06/12/keep-directory-structure-when-uploading/ 。
- $("#btn").click(function(){
- uploadFiles($("#multi_upload")[0].files);
- });
- function uploadFiles(files){
- // Create a new HTTP requests, Form data item (data we will send to the server) and an empty string for the file paths.
- xhr = new XMLHttpRequest();
- data = new FormData();
- var paths = new Array();
- // Set how to handle the response text from the server
- xhr.onreadystatechange = function(ev){
- if (xhr.readyState == 4){
- $("#info").html(xhr.responseText);
- }
- };
- // Loop through the file list
- for (var i in files){
- if (typeof files[i] != 'object'){
- continue;
- }
- // Append the current file path to the paths variable (delimited by tripple hash signs - ###)
- paths.push(files[i].webkitRelativePath);
- //paths += files[i].webkitRelativePath+"###";
- // Append current file to our FormData with the index of i
- data.append(i, files[i]);
- };
- // Append the paths variable to our FormData to be sent to the server
- // Currently, As far as I know, HTTP requests do not natively carry the path data
- // So we must add it to the request manually.
- data.append('paths', paths);
- // Open and send HHTP requests to upload.php
- xhr.open('POST', "/bgidb/Data/multiuploadify", true);
- xhr.send(this.data);
- }
【2】已完成:文件保存到了后台,文件相对路径传给了后台。现在处理将文件相对路径剖开成文件夹的树形结构。树形结构中每个非根节点都有一个父节点,每个叶子结点都没有孩子。所以只要记录下来每个节点的id,和父亲的id,就能存储和读取一个树形结构了。
当然这里面依旧有trick。首先考虑一下你的文件相对路径长什么样子,如这样:“images/folder1/folder2/xxx.jpg”。当然你可以dump到前台自己看看。“/”符号将每个节点隔开了,所以只要把所有这样的相对路径分离开,你就得到了每一个文件夹(文件)的名字,每一个文件夹(文件)的父亲。explode函数能够完成隔开节点,以及生成节点的功能。接下来就是一个节点一个节点地把树形结构写到数据库。
- protected function insertFolderInfo(){
- //插入文件夹层级信息
- $reletivePath = explode(',',I('post.paths'));//把传到后台的paths变成数组
- $flag = true;
- foreach ($reletivePath as $key => $path) {
- $pathTree = explode('/',$path);
- $insertId = 0;
- $octFolder = M("octfolder");
- foreach ($pathTree as $key => $nodeName) {
- if ($key == 0){//舍去最高层目录
- continue;
- }
- $leaf = 0;
- if ($key == sizeof($pathTree) - 1){
- $leaf = 1;
- }
- $nodeAdd = array(
- 'nodeName' => $nodeName,
- 'parent' => $insertId,
- 'leaf' => $leaf
- );
- $re = $octFolder->where($nodeAdd)->find();
- if (!$re){
- $insertId = $octFolder->add($nodeAdd);
- if (!$insertId){
- $flag = false;
- break;
- }
- }
- else{
- $insertId = $re['id'];
- }
- }
- if ($flag == false){
- break;
- }
- }
- return $flag;
- }
【3】树形结构也保存到了数据库。接下来从前台优雅的把它取出来。当看目录里的文件时,当前目录里的一级文件夹(文件)的父节点都是一样的,都是当前目录的id。所有只要定义一个query函数,每次显示一个id的所有孩子,事情就简单了。剩下的就是页面的更新,触发器的绑定(为了更好的操作体验)。
- function query(id){
- $.post(
- '/bgidb/oct/octFileAjax',
- {parent : id},
- function(data){
- //console.log(data);
- if(data['wrongcode']==999){
- backStepId = data['backStepId'];
- var files = data['files'];
- filePanel.html(formatFiles(files));
- boundListener();
- }else{
- alert(data['wrongmsg']);
- }
- },
- "json");
- }
后台只要根据这个parent参数去找到所有的子节点,然后传给前台,是图片的附个图片链接,然后还附上上一级目录的id以便回退就ok。前台接到了data之后,就把前端的文件夹都画出来,触发器绑好。
- public function octFileAjax(){
- $WRONG_CODE = C('WRONG_CODE');
- $WRONG_MSG = C('WRONG_MSG');
- $data['wrongcode'] = $WRONG_CODE['totally_right'];
- $parent = I("post.parent", null);
- if ($parent == null){
- $data['wrongcode'] = $WRONG_CODE['query_data_invalid'];
- }
- else{
- $oF = M("octfolder");
- $cond = array(
- 'parent' => $parent
- );
- $re = $oF->where($cond)->select();
- if (!$re){
- $data['wrongcode'] = $WRONG_CODE['not_exist'];
- }
- else{
- foreach ($re as $key => $file) {
- if($file['leaf'] == 1){
- $oct = M("oct");
- $cond = array(
- 'name' => $file['nodename']
- );
- $img = $oct->field('imgsite')->where($cond)->find();
- $re[$key]['imgsite'] = $img['imgsite'];
- }
- }
- $data['files'] = $re;
- $oF = M("octfolder");
- $cond = array(
- 'id' => $parent
- );
- $backStepId = $oF->field('parent')->where($cond)->find();
- if (!$backStepId){
- $data['backStepId'] = 0;
- }
- else{
- $data['backStepId'] = $backStepId['parent'];
- }
- }
- }
- $data['wrongmsg'] = $WRONG_MSG[$data['wrongcode']];
- $this->ajaxReturn($data);
- }
完整浏览页面前端实现:这里实在不想自己画文件夹的前端了,就用了MetroUI里的图标和样式。
我自己完整实现:
- <!DOCTYPE html>
- <html>
- <head>
- <title>OCT文件浏览系统</title>
- <link href="__PUBLIC__/metro/css/metro.min.css" rel="stylesheet">
- <script src="__PUBLIC__/js/jquery-2.1.1.min.js" type="text/javascript"></script>
- <script src="__PUBLIC__/metro/js/metro.min.js" type="text/javascript"></script>
- <script type="text/javascript">
- $(document).ready(function(){
- var filePanel = $("#filePanel");
- var backStepId = 0;
- query(0);
- function query(id){
- $.post(
- '/bgidb/oct/octFileAjax',
- {parent : id},
- function(data){
- //console.log(data);
- if(data['wrongcode']==999){
- backStepId = data['backStepId'];
- var files = data['files'];
- filePanel.html(formatFiles(files));
- boundListener();
- }else{
- alert(data['wrongmsg']);
- }
- },
- "json");
- }
- function formatFolder(file){
- //backStepId = file.parent;
- return '<div class="list octFolder" folderId="' + file.id + '" folderParentId="' + file.parent + '" ><img src="/Public/metro/images/folder-images.png" class="list-icon"><span class="list-title">'+ file.nodename +'</span></div>';
- }
- function formatImg(file){
- return '<div class="list octImage" name="' + file.nodename + '"><img src="' + file.imgsite + '" class="list-icon"><span class="list-title">'+ file.nodename +'</span></div>';
- }
- function formatStepBack(){
- return '<div class="list stepBack"><span class="list-icon icon-font-icon">..</span><span class="list-title">上级目录</span></div>';
- }
- function formatFiles(files){
- var re = "";
- re += formatStepBack(files);
- for(var i = 0; i < files.length; i++){
- var file = files[i];
- if(file.leaf == 0){
- re += formatFolder(file);
- }
- else{
- re += formatImg(file);
- }
- }
- return re;
- }
- function boundListener(){
- $(".octImage").click(function(){
- var nodeName = $(this).attr('name');
- $("#input").val(nodeName);
- $("#uploadAgent").submit();
- })
- $(".octFolder").click(function(){
- var folderId = $(this).attr('folderId');
- query(folderId);
- })
- $(".stepBack").click(function(){
- query(backStepId);
- })
- }
- });
- </script>
- </head>
- <body>
- <div class="app-bar">
- <a class="app-bar-element" href="__MODULE__/oct/octFileSys">OCT文件浏览系统</a>
- <span class="app-bar-divider"></span>
- <a class="app-bar-element" href="/">回到首页</a>
- </div>
- <form action="{:U('Bgidb/Oct/oct')}" enctype="multipart/form-data" method="post" id="uploadAgent" target='_blank' ">
- <input type="hidden" name="imageName" id="input"/>
- </form>
- <div class="listview " id="filePanel">
- </div>
- </body>
- </html>
这个其实是一个很大的系统的一部分,其它部分也都是我一直维护的,快两年了。
最后自己对于安全方面没有做工作,因为我对这个领域没有概念,我只是在理解力范围内随手减少代码的臃肿。
- HTML5+PHP 实现 保存文件夹相对路径 递归上传 在线浏览
- HTML5+PHP 实现 保存文件夹相对路径 递归上传 在线浏览
- MFC 浏览文件夹,浏览保存文件, 获取当前路径
- MFC浏览文件夹,浏览保存文件, 获取当前路径
- MFC 浏览文件夹,浏览保存文件, 获取当前路径
- MFC 浏览文件夹,浏览保存文件, 获取当前路径
- php 实现相对路径函数
- 网站的文件的上传,并将相对路径保存到数据库的代码实现。
- VB6.0 浏览文件夹对话框 选择文件保存路径
- Struts2实现文件上传(相对路径)
- 浏览获得文件夹路径
- MFC浏览文件夹路径
- 相对文件夹路径
- PHP文件类-上传,样图,文件夹内容浏览删除
- HTML5在线摄像头拍照上传并保存到服务器
- MFC实现打开、保存文件对话框和浏览文件夹对话框
- MFC实现打开、保存文件对话框和浏览文件夹对话框
- MFC实现打开、保存文件对话框和浏览文件夹对话框
- 条款 34
- **js刷新页面方法大全**
- 一个杭州HR的国际招聘感悟
- [matlab]石头剪刀布
- 一个表单使用两个提交按钮
- HTML5+PHP 实现 保存文件夹相对路径 递归上传 在线浏览
- 两个window.onload = function(){}不全部加载
- 设计模式
- 告别短信验证时代的先驱者
- Storm<配置文件的坑>
- Oracle 开发
- 快速上手Aspect
- php实现按文件名搜索文件的远程文件查找器
- oracle12 关于数据库pdb cdb切换