一直想做一个类似 Windows 命令行中 del 命令删除文件的功能,它支持 环境变量通配符可以递归,后来发现自己写这么一个小功能还真的不是一件容易的事情,没办法为了着急使用先临时做了一个小版本。代码有些缺憾。

  • 不支持环境变量
  • 不支持固定后缀文件递归删除
// example.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include <windows.h>
#include <iostream>


BOOL DeleteFiles(const std::wstring file_full_path)
{
    BOOL no_error = TRUE;
    WIN32_FIND_DATA win32_find_data = { 0 };

    std::wstring dir = file_full_path.substr(0, file_full_path.rfind(_T("\\"))).c_str();
    std::wstring file = file_full_path.substr(file_full_path.rfind(_T("\\")) + 1, file_full_path.length());

    if (dir.size() == 0 || file.size() == 0)
    {
        return FALSE;
    }

    HANDLE handle = FindFirstFile(file_full_path.c_str(), &win32_find_data);
    if (INVALID_HANDLE_VALUE == handle)
    {
        return no_error;
    }

    do
    {
        // 如果是目录递归操作
        if (win32_find_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
        {
            // 排除 . 和 .. 两个文件夹
            if (_tcsicmp(_T("."), win32_find_data.cFileName) != 0 &&
                _tcsicmp(_T(".."), win32_find_data.cFileName) != 0)
            {
                // 根目录加上搜索出来的目录
                std::wstring new_full_path = dir;
                new_full_path += _T("\\");
                new_full_path += win32_find_data.cFileName;

                // 备份搜索出来的目录完整路径用以删除
                std::wstring new_dir = new_full_path;

                // 再加上要删除的文件名
                new_full_path += _T("\\");
                new_full_path += file;

                // 开始删除
                if (DeleteFiles(new_full_path))
                {
                    // 删除子文件后删除整个目录
                    RemoveDirectory(new_dir.c_str());
                }
            }
        }
        else
        {
            std::wstring full_file_name = dir;
            full_file_name += _T("\\");
            full_file_name += win32_find_data.cFileName;

            // 去除只读文件的只读属性
            DWORD file_attr = GetFileAttributes(full_file_name.c_str());
            if ((file_attr & FILE_ATTRIBUTE_READONLY) != 0)
            {
                SetFileAttributes(full_file_name.c_str(), file_attr & (~FILE_ATTRIBUTE_READONLY));
            }

            BOOL del_res = DeleteFiles(full_file_name.c_str());
            if (del_res)
            {
                std::wcout << full_file_name.c_str() << std::endl;
            }

            // 如果有一个文件删除失败则返回上层,上层若发现有删除失败的文件则不删除其斧文件夹
            if (del_res == FALSE && no_error == TRUE)
            {
                no_error = FALSE;
            }
        }
    } while (FindNextFile(handle, &win32_find_data) != 0);

    FindClose(handle);
    return no_error;
}

int _tmain(int argc, _TCHAR* argv[])
{
    _wsetlocale(LC_ALL, L"chs");

    DeleteFiles(_T(R"(C:\Users\ADMINI~1\AppData\Local\Temp\*)"));

    return 0;
}

你有没有遇到过这样的一些场景,在某些时候你需要给一个新系统部署一些自己需要的软件和运行环境,而系统磁盘中有这个软件曾经下载的多个安装包副本,不知道安装哪个最好,每次都要从网络上重新下载。还有一些时候更换了自己不熟悉的环境,自己电脑又不在身边,想找一个自己习惯使用的软件到处搜索都搜索不到,不是版本不对就是下载地址失效了。因为以上种种情况和一些其他的重要原因,我打算开发一个自己使用的软件下载站,由自己更新和维护里面的软件,确保软件只有一份,只保留最新(但可以看到历史版本),需要的时候去下载安装就好,不怕病毒、不怕下载地址失效、不怕找不到某个软件。我想每个人都需要一个这样属于自己的下载站,所以就产生了“当漏”。与此同时,我也希望能帮助一些不愿意或者不熟悉软件更新流程的人,提供一个安全、稳定的下载站点。

站点地址

当漏:https://download.mycode.net.cn/

使用资源

因为需要一个稳定的下载服务器资源,同时该服务器要支持我们上传文件,国内无疑就只有阿里云 OSS、七牛对象储存等类似这些资源了,因为七牛有成熟的 SDK 封装(qiniu4js)和每个月免费的流量,所以我选择了七牛作为资源管理服务器。不用为资源丢失等情况烦心了。

站点截图

站点功能

目前站点已经开发完成了基本的上传和下载功能,但部分功能还不完善,计划中我将实现以下功能来丰富和完善这个下载站:

  • 周边社交功能(软件评论、分享等)
  • 管理员后台管理功能(用户管理、软件添加更新等)
  • 历史版本功能
  • 用户积分系统

代码托管

该下载站所有代码均托管在 github 上,代码完全开放,也希望有志之士能与我们一起参与到开发的队伍中,完善这个下载站。

锚点是通过在界面中增加一些特征(比如 id),然后在 URL 地址后面加上 #id 就可以访问到指定页面的指定位置,这样可以让我们快速跳转到页面的某个位置,但是在 react-router 中这种方法遇到了问题,因为 react-router 会把 # 当做是 hash 来处理。导致即使跳转到指定页面后,# 后面的锚点也不生效。针对这个问题,在 react-router 的一个 issue 中大家也展开了激烈的讨论。以下是我看过以后整理的几种解决办法。

继续阅读

本文阐述了如何在一个使用了 react-router 的 react 项目中合理的使用 antd-mobile tabbar 功能。在 antd-mobile 官方的例子中可以看到,只需要将不同的组件放置到每个 TabBar.Item 里面就可以了,这样就可以实现简单的切换效果,但是存在几个问题。

一个是切换过程中,路由是不会跟着切换的。比如我们想分享一个地址,当其他人打开这个地址时自动就跳转到第二个 tab 上。如果按上面的方法做是无法实现的。

另外一个问题是这样的设计不太符合大型项目的框架设计,我们往往会制作一些 layouts,给不同的组件匹配不同的 layout。如果按上面介绍的方法做,也是不好实现的。

综合以上两点问题,再加上 Google 了一些资料后,写下本文,以帮助更多遇到类似问题的人。

解决方案

首先定义四个路由分别指定不同的 component,要注意的是这四个路由都统一使用一个 layout,这也就解决了一些大型项目中分多种 layout 的问题。如下代码所示:

<Router history={browserHistory}>
  {/* MainLayout 中包含了 antd-mobile tabbar */}
  <Route path='/' component={MainLayout}>
    {/* 默认跳转到 questions 页面 */}
    <IndexRedirect to='/questions' />
    <Route path='/questions' component={Questions} />
    <Route path='/activities' component={Activities} />
    <Route path='/videos' component={Videos} />
    <Route path='/mine' component={Mine} />
  </Route>
</Router>

随后我们看一下 mainLayout 的代码:

const MainLayout = ({children}) => {
  const pathname = children.props.location.pathname

  return (
    <TabBar
      unselectedTintColor='#949494'
      tintColor='#33A3F4'
      barTintColor='white'
    >
      <TabBar.Item
        title='问答'
        key='questions'
        icon={<div className='questions-icon' />}
        selectedIcon={<div className='questions-selected-icon' />}
        selected={pathname === '/questions'}
        onPress={() => {
          browserHistory.push('/questions')
        }}
      >
        { pathname === '/questions' ? children : null }
      </TabBar.Item>
      <TabBar.Item
        title='活动'
        key='activities'
        icon={<div className='activities-icon' />}
        selectedIcon={<div className='activities-selected-icon' />}
        selected={pathname === '/activities'}
        onPress={() => {
          browserHistory.push('/activities')
        }}
      >
        { pathname === '/activities' ? children : null }
      </TabBar.Item>
      <TabBar.Item
        title='视频'
        key='videos'
        icon={<div className='videos-icon' />}
        selectedIcon={<div className='videos-selected-icon' />}
        selected={pathname === '/videos'}
        onPress={() => {
          browserHistory.push('/videos')
        }}
      >
        { pathname === '/videos' ? children : null }
      </TabBar.Item>
      <TabBar.Item
        title='我的'
        key='mine'
        icon={<div className='mine-icon' />}
        selectedIcon={<div className='mine-selected-icon' />}
        selected={pathname === '/mine'}
        onPress={() => {
          browserHistory.push('/mine')
        }}
      >
        { pathname === '/mine' ? children : null }
      </TabBar.Item>
    </TabBar>
  )
}

这里重点的代码就是 pathname === '/questions' ? children : null,根据当前路由判断加载不同的 component,并且在点击任何一个按钮的时候,自动跳转到指定的路由上。其中 selected 属性也根据路由动态的变换样式。路由传递给 mainLayout 是一个 children,这个 children 中就包含了组件的信息,我们根据路由的不同加载即可。

总结

这样处理后无论我们直接访问 URL 还是点击 tabbar 下面的任意按钮,不但可以切换页面,路由也会随之变动。最重要的是我们套用了 layout,让项目看起来更加合理。

在远程工作中,并不是所有项目都是从头开始的,有很多项目是已经做了一部分,或者需要按着其要求来创建项目和编码的。所以这其中就有一些公司或者团队会使用一些代码规范,以保证无论是公司内部还是远程工作的同事都可以保持一致的代码规范,让代码不会过于凌乱。

继续阅读