了解一个项目,恐怕首先都是通过其Readme文件了解信息。如果你以为Readme文件都是随便写写的那你就错了。github,oschina git gitcafe的代码托管平台上的项目的Readme.MD文件都是有其特有的语法的。称之为Markdown语法。基本规则如下:

Markdown 语法速查表
1 标题与文字格式
标题
# 这是 H1 <一级标题>
## 这是 H2 <二级标题>
###### 这是 H6 <六级标题>
文字格式
**这是文字粗体格式**
*这是文字斜体格式*
~~在文字上添加删除线~~
2 列表
无序列表
* 项目1
* 项目2
* 项目3
有序列表
1. 项目1
2. 项目2
3. 项目3
   * 项目1
   * 项目2
3 其它
图片
![图片名称](http://gitcafe.com/image.png)
链接
[链接名称](http://gitcafe.com)
引用
> 第一行引用文字
> 第二行引用文字
水平线
***
代码
`<hello world>`
代码块高亮
“`ruby
  def add(a, b)
    return a + b
  end
“`
表格
  表头  | 表头
  ————- | ————-
 单元格内容  | 单元格内容
 单元格内容l  | 单元格内容

如果直接记语法,那似乎困难了些。这里OneCoder推荐两个Markdown的编辑器。

在线编辑器:stackedit
网址:https://stackedit.io/

Mac下离线编辑器Mou
下载地址:http://mouapp.com/

OneCoder这里使用的是后者为自己的shurnim-storage项目写Readme。至于这个项目是什么,见Readme文档,OneCoder也会在另外的博文做一些补充说明。成品Readme如下:

# shurnim-storage

![Shurnim icon](http://onecoder.qiniudn.com/8wuliao/DLPii2Jx/rEBO.jpg)

## 目录
* [背景介绍](#背景介绍)
* [项目介绍](#项目介绍)
* [使用说明](#使用说明)
  * [获取代码](#获取代码)
  * [开发插件](#开发插件)
  * [使用ShurnimStorage接口](#使用ShurnimStorage接口)
       * [接口介绍](#接口介绍)
       * [使用样例](#使用样例)
* [其他](#其他)

<a name="背景介绍"></a>
## 背景介绍

*Shurnim*,是我和我老婆曾经养过的一只仓鼠的名字。<br/>
*shurnim-storage*,是一个插件式云存储/网盘同步管理工具。是在参加又拍云开发大赛的过程中设计并开发。

<a name="项目介绍"></a>
## 项目介绍

*shurnim-storage* 的设计初衷是给大家提供一个可方便扩展的云存储/网盘同步工具。分后端接口和前端UI界面两部分。<br>

由于目前各种云存储和网盘系统层出不穷,单一工具往往支持支持某几个特定存储之间的同步,如**又拍云**到**七牛云存储**的同步工具,此时如若想同步到其他存则可能需要新的工具,给用户带来不便。*shurnim-storage*  正是为了解决此问题而设计的。

在*shurnim-storage*中,用户使用的固定的统一的后端接口。而所有云存储/网盘API的支持则是以插件的形式部署到系统中的。如此,如果用户想要一个从**又拍云**到**Dropbox**的同步工具,则只需要在原有基础上,增加**Dropbox**的插件,即可实现互通,方便快捷。<br/>

同时,后端统一接口的设计也考虑到界面开发的需求,可直接通过后端提供的接口开发具有上述扩展功能的云存储UI工具。<br>

目前,后端整体框架的核心部分已经基本开发完成。只需逐步补充后端接口和插件开发接口的定义即可。但由于个人时间和能力所限,UI部分没有开发,有兴趣的同学可以一试。

<a name="使用说明"></a>
## 使用说明

<a name="获取代码"></a>
### 获取代码

* gitcafe项目主页: <https://gitcafe.com/onecoder/shurnim-storage-for-UPYUN>
* OSChina项目主页: <http://git.oschina.net/onecoder/shurnim-storage><br>
OSChina上的会持续更新。

另外你也可以通过OSChina的Maven库获取依赖,或者自己编译jar包。

* maven

1. 加入OSC仓库
   
                    <repositories>
                      <repository>
                           <id>nexus</id>
                           <name>local private nexus</name>
                           <url>http://maven.oschina.net/content/groups/public/</url>
                           <releases>
                                <enabled>true</enabled>
                           </releases>
                           <snapshots>
                                <enabled>false</enabled>
                           </snapshots>
                      </repository>
                 </repositories>

2. 加入依赖
   
               <dependency>
                 <groupId>com.coderli</groupId>
                 <artifactId>shurnim-storage</artifactId>
                  <version>0.1-alpha</version>
               </dependency>
* Gradle 编译Jar

在项目目录执行
   
     gradle jar
   
<a name="开发插件"></a>
### 开发插件

在*shurnim-storage*中,插件就像一块一块的积木,不但支撑着框架的功能,也是框架可扩展性的基石。开发一个插件,仅需两步:

1. 实现PluginAPI接口

“`
package com.coderli.shurnim.storage.plugin;

import java.io.File;
import java.util.List;

import com.coderli.shurnim.storage.plugin.model.Resource;

/**
* 各种云存储插件需要实现的通用接口
*
* @author OneCoder
* @date 2014年4月22日 下午9:43:41
* @website http://www.coderli.com
*/
public interface PluginAPI {

/**
      * 初始化接口
      *
      * @author OneCoder
      * @date 2014年5月19日 下午10:47:40
      */
     void init();

/**
      * 获取子资源列表
      *
      * @param parentPath
      * @return
      * @author OneCoder
      * @date 2014年4月24日 下午11:29:14
      */
     List<Resource> getChildResources(String parentPath);

/**
      * 下载特定的资源
      *
      * @param parentPath
      *            目录路径
      * @param name
      *            资源名称
      * @param storePath
      *            下载资源保存路径
      * @return
      * @author OneCoder
      * @date 2014年4月24日 下午11:30:19
      */
     Resource downloadResource(String parentPath, String name, String storePath);

/**
      * 创建文件夹
      *
      * @param path
      *            文件夹路径
      * @param auto
      *            是否自动创建父目录
      * @return
      * @author OneCoder
      * @date 2014年5月15日 下午10:10:04
      */
     boolean mkdir(String path, boolean auto);

/**
      * 上传资源
      *
      * @param parentPath
      *            父目录路径
      * @param name
      *            资源名称
      * @param uploadFile
      *            待上传的本地文件
      * @return
      * @author OneCoder
      * @date 2014年5月15日 下午10:40:13
      */
     boolean uploadResource(String parentPath, String name, File uploadFile);
}
“`

目前插件的接口列表仅为同步资源设计,如果想要支持更多操作(如删除,查找等),可扩展该接口定义。<br/><br/>
接口中,所有的参数和返回值均为*shurnim-storage*框架中定义的通用模型。因此,您在开发插件过程中需要将特定SDK中的模型转换成接口中提供的模型。<br/><br/>
插件实现类只要与*shurnim-storage*工程在同一个classpath即可使用。您既可以直接在源码工程中开发插件,就如工程里提供的*upyun*和*qiniu*插件一样,也可以作为独立工程开发,打成jar,放置在同一个classpath下。<br/><br/>
*upyun*插件样例(功能不完整):

“`  
package com.coderli.shurnim.storage.upyun.plugin;

import java.io.File;
import java.util.List;

import com.coderli.shurnim.storage.plugin.AbstractPluginAPI;
import com.coderli.shurnim.storage.plugin.model.Resource;
import com.coderli.shurnim.storage.plugin.model.Resource.Type;
import com.coderli.shurnim.storage.upyun.api.UpYun;

public class UpYunPlugin extends AbstractPluginAPI {

private UpYun upyun;
     private String username;
     private String password;
     private String bucketName;

public UpYun getUpyun() {
          return upyun;
     }

public void setUpyun(UpYun upyun) {
          this.upyun = upyun;
     }

public String getUsername() {
          return username;
     }

public void setUsername(String username) {
          this.username = username;
     }

public String getPassword() {
          return password;
     }

public void setPassword(String password) {
          this.password = password;
     }

public String getBucketName() {
          return bucketName;
     }

public void setBucketName(String bucketName) {
          this.bucketName = bucketName;
     }

/*
      * (non-Javadoc)
      *
      * @see
      * com.coderli.shurnim.storage.plugin.PluginAPI#getChildResources(java.lang
      * .String)
      */
     @Override
     public List<Resource> getChildResources(String parentPath) {
          return null;
     }

/*
      * (non-Javadoc)
      *
      * @see
      * com.coderli.shurnim.storage.plugin.PluginAPI#downloadResource(java.lang
      * .String, java.lang.String, java.lang.String)
      */
     @Override
     public Resource downloadResource(String parentPath, String name,
               String storePath) {
          File storeFile = new File(storePath);
//          if (!storeFile.exists()) {
//               try {
//                    storeFile.createNewFile();
//               } catch (IOException e) {
//                    e.printStackTrace();
//               }
//          }
          String filePath = getFullPath(parentPath, name);
          upyun.readDir("/api");
          if (upyun.readFile(filePath, storeFile)) {
               Resource result = new Resource();
               result.setName(name);
               result.setPath(parentPath);
               result.setType(Type.FILE);
               result.setLocalFile(storeFile);
               return result;
          }
          return null;
     }

String getFullPath(String parentPath, String name) {
          if (!parentPath.endsWith(File.separator)) {
               parentPath = parentPath + File.separator;
          }
          return parentPath + name;
     }

/*
      * (non-Javadoc)
      *
      * @see com.coderli.shurnim.storage.plugin.PluginAPI#mkdir(java.lang.String,
      * boolean)
      */
     @Override
     public boolean mkdir(String path, boolean auto) {
          // TODO Auto-generated method stub
          return false;
     }

/*
      * (non-Javadoc)
      *
      * @see
      * com.coderli.shurnim.storage.plugin.PluginAPI#uploadResource(java.lang
      * .String, java.lang.String, java.io.File)
      */
     @Override
     public boolean uploadResource(String parentPath, String name,
               File uploadFile) {
          // TODO Auto-generated method stub
          return false;
     }

/*
      * (non-Javadoc)
      *
      * @see com.coderli.shurnim.storage.plugin.AbstractPluginAPI#init()
      */
     @Override
     public void init() {
          upyun = new UpYun(bucketName, username, password);
     }

}
“`

2. 编写插件配置文件

“`
<?xml version="1.0" encoding="UTF-8"?>
<plugin>
     <id>qiniu</id>
     <name>七牛云存储</name>
     <api>
          <className>com.coderli.shurnim.storage.qiniu.QiniuPlugin</className>
          <params>
               <param name="access_key" displayName="ACCESS_KEY">EjREKHI_GFXbQzyrKdVhhXrIRyj3fRC1s9UmZPZO
               </param>
               <param name="secret_key" displayName="SECRET_KEY">88NofFWUvkfJ6T6rGRxlDSZOQxWkIxY2IsFIXJLX
               </param>
               <param name="bucketName" displayName="空间名">onecoder
               </param>
          </params>
     </api>
</plugin>
“`
   * **id** 为该插件在*shurnim-storage*框架下的唯一标识,不可重复,必填。
    * **name** 为显示值,为UI开发提供可供显示的有语义的值。
    * **className** 为插件接口实现类的完整路径。必填
    * **params/param** 为插件需要用户配置的参数列表。其中
         * *name* 代表参数名,需要与接口实现类中的参数名严格一致,且必须有相应的set方法的格式要求严格,即set+首字母大写的参数名。例如:setAccess_key(String arg); 目前只支持*String*类型的参数。
         * *displayName* 为参数显示名,同样是为了UI开发的考虑,方便用户开发出可根据参数列表动态显示的UI界面。
         * 参数的值可以直接配置在配置文件中,也可以在运行期动态赋值。直接配置值,对于直接使用后端接口来说较为方便。对于UI开发来说,运行期动态赋值更为合理。<br/></br>

在使用源码工程时,插件配置文件统一放置在工程的*plugins*目录下。你也可以统一放置在任何位置。此时,在构造后端接口实例时,需要告知接口该位置。
   
<a name="使用ShurnimStorage接口"></a>
### 使用*ShurnimStorage*接口

<a name="接口介绍"></a>
#### 接口介绍

**ShurnimStorage**接口是*shurinm-storage*框架全局的也是唯一的接口,目前定义如

“`
package com.coderli.shurnim.storage;

import java.util.List;
import java.util.Map;

import com.coderli.shurnim.storage.plugin.model.Plugin;
import com.coderli.shurnim.storage.plugin.model.Resource;

/**
* 后台模块的全局接口<br>
* 通过该接口使用后台的全部功能。<br>
* 使用方式:<br>
* <li>
* 1.先通过{@link #getSupportedPlugins()}方法获取所有支持的平台/插件列表。 <li>
* 2.将列表中返回的ID传入对应的接口参数中,进行对应的平台的相关操作。<br>
* 需要注意的是,不同平台的插件需要给不同的参数赋值,该值可以直接配置在配置文件中。<br>
* 也可以在运行期动态赋值。(会覆盖配置文件中的值。)<br>
*
* 参数列表的设计,方便UI开发人员动态的根据参数列表生成可填写的控件。并给参数赋值。增强了可扩展性。
*
* @author OneCoder
* @date 2014年4月22日 下午9:21:58
* @website http://www.coderli.com
*/
public interface ShurnimStorage {

/**
      * 获取当前支持的插件列表<br>
      * 没有支持的插件的时候可能返回null
      *
      * @return
      * @author OneCoder
      * @date 2014年5月7日 下午8:53:25
      */
     List<Plugin> getSupportedPlugins();

/**
      * 给指定的插件的对应参数赋值<br>
      * 此处赋值会覆盖配置文件中的默认值
      *
      * @param pluginId
      *            插件ID
      * @param paramsKV
      *            参数键值对
      * @author OneCoder
      * @date 2014年5月9日 上午12:41:53
      */
     void setParamValues(String pluginId, Map<String, String> paramsKV);

/**
      * 获取插件对应目录下的资源列表
      *
      * @param pluginId
      *            插件ID
      * @param path
      *            指定路径
      * @return
      * @author OneCoder
      * @date 2014年5月11日 上午8:52:00
      */
     List<Resource> getResources(String pluginId, String path);

/**
      * 同步资源
      *
      * @param fromPluginId
      *            待同步的插件Id
      * @param toPluginIds
      *            目标插件Id
      * @param resource
      *            待同步的资源
      * @return 同步结果
      * @author OneCoder
      * @date 2014年5月11日 上午11:41:24
      */
     boolean sycnResource(String fromPluginId, String toPluginId,
                    Resource resource) throws Exception;
}
“`

当前接口实际仅包含了获取资源列表*getResources*和同步资源*sycnResource*功能,*getSupportedPlugins*和*setParamValues*实际为辅助接口,在UI开发时较为有用。<br/><br/>
同样,您也可以扩展开发该接口增加更多的您喜欢的特性。例如,同时删除给定存储上的文件。当然,这需要插件接口的配合支持。<br/><br/>

这里,*sycnResource*设计成插件间一对一的形式,是考虑到获取同步是否成功的结果的需求。如果您想开发一次同步到多个存储的功能,建议您重新开发您自己的接口实现类,因为默认实现会多次下次资源(每次同步后删除),造成网络资源的浪费。

接口的默认实现类是: **DefaultShurnimStorageImpl**

<a name="使用样例"></a>
#### 使用样例
“`      
package com.coderli.shurnim.test.shurnimstorage;

import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.Test;

import com.coderli.shurnim.storage.DefaultShurnimStorageImpl;
import com.coderli.shurnim.storage.ShurnimStorage;
import com.coderli.shurnim.storage.plugin.model.Resource;
import com.coderli.shurnim.storage.plugin.model.Resource.Type;

/**
* 全局接口测试类<br>
* 时间有限,目前仅作整体接口测试。细粒度的单元测试,随开发补充。
*
* @author OneCoder
* @date 2014年5月19日 下午10:50:27
* @website http://www.coderli.com
*/
public class ShurnimStorageTest {

private static ShurnimStorage shurnim;

@BeforeClass
     public static void init() {
          shurnim = new DefaultShurnimStorageImpl(
                    "/Users/apple/git/shurnim-storage-for-UPYUN/plugins");
     }

@Test
     public void testSycnResource() {
          Resource syncResource = new Resource();
          syncResource.setPath("/api");
          syncResource.setName("api.html");
          syncResource.setType(Type.FILE);
          try {
               Assert.assertTrue(shurnim.sycnResource("upyun", "qiniu",
                         syncResource));
          } catch (Exception e) {
               e.printStackTrace();
          }
     }
}
“`
<a name="其他"></a>
## 其他

时间仓促,功能简陋,望您包涵。OneCoder(Blog:[http://www.coderli.com](http://www.coderli.com))特别希望看到该项目对您哪怕一点点的帮助。任意的意见和建议,欢迎随意与我沟通,联系方式:

* Email: <wushikezuo@gmail.com>
* QQ:57959968
* Blog:[OneCoder](http://www.coderli.com)

项目的Bug和改进点,可在OSChina上以issue的方式直接提交给我。

效果预览:

 

如非特别注明,本站内容均为OneCoder原创,转载请务必注明作者和原始出处。
本文地址:http://www.coderli.com/write-readme-for-your-project

为项目编写Readme.MD文件的更多相关文章

  1. 原来Github上的README.md文件这么有意思——Markdown语言详解

    转载请注明出处:http://blog.csdn.net/zhaokaiqiang1992 之前一直在使用github,也在上面分享了不少的项目和Demo,每次创建新项目的时候,使用的都是默认的REA ...

  2. 【转录】原来Github上的README.md文件这么有意思——Markdown语言详解

    之前一直在使用github,也在上面分享了不少的项目和Demo,每次创建新项目的时候,使用的都是默认的README.md文件,也不曾对这个文件有过什么了解.但是在看到别人写的项目的README.md里 ...

  3. GitHub中README.md文件的编辑和使用

    最近对它的README.md文件颇为感兴趣.便写下这贴,帮助更多的还不会编写README文件的同学们. README文件后缀名为md.md是markdown的缩写,markdown是一种编辑博客的语言 ...

  4. 关于github中的README.md文件

    0x01 README.md文件是用Markdown语言编写的,md=Markdown; 在线编辑工具: https://stackedit.io/editor# https://maxiang.io ...

  5. 解决:GitHub 远程端添加了 README.md 文件后,本地 push 代码时出现错误

    一.错误描述 To github.com:compassblog/PythonExercise.git ! [rejected] master -> master (fetch first) e ...

  6. Git学习:如何在Github的README.MD文件下添加图片

    格式如下: ![image](图片的绝对路径) 关于图片的绝对路径: 必须把图片上传到github的代码仓库里,再将其图片的网址复制到括号里才可以,不能够直接把图片复制到readme.md文件里面,这 ...

  7. 原来Github上的README.md文件这么有意思——Markdown语言详解(sublime text2 版本)

    一直想学习 Markdown 语言,想起以前读的一篇 赵凯强 的 博客 <原来Github上的README.md文件这么有意思——Markdown语言详解>,该篇博主 使用的是Mac系统, ...

  8. 【GitHub】给GitHub上的ReadMe.md文件中添加图片怎么做 、 gitHub创建文件夹

    1.首先在github上的仓库上,创建一个空的文件夹,用于上传图片 上图看 要点击的按钮是创建新的文件,并不是创建新的文件夹,具体怎么?往下看 这个时候,下面的提交按钮才能提交 2.进入新创建的文件夹 ...

  9. pycharm(社区版2019.1版本)打开README.md文件卡死解决办法

    现象:pycharm(社区版2019.1版本)打开README.md文件卡死 解决办法: 将插件Markdown support前的勾选√去掉,保存修改后重启pycharm即可

随机推荐

  1. 【转】Objective-C消息机制的原理

    转自:http://dangpu.sinaapp.com/?p=119 在Objective-C中,message与方法的真正实现是在执行阶段绑定的,而非编译阶段.编译器会将消息发送转换成对objc_ ...

  2. 英文长单词断行 word-break VS word-wrap

    你真的了解word-wrap和word-break的区别吗? 这两个东西是什么,我相信至今还有很多人搞不清,只会死记硬背的写一个word-wrap:break-word;word-break:brea ...

  3. Action重定向总结

    [HttpPost] public ActionResult StudentList( string StudName, string studName, DateTime BirthDay, For ...

  4. Jsp request

    <%@ page language="java" import="java.util.*" pageEncoding="GB18030" ...

  5. 临时解决linux下time wait问题

     通过 netstat  -anp | grepTIME_WAIT | wc -l 命令查看数量,发现TIME_WAIT的连接数量超过了阈值   1.初步怀疑是程序没有关闭连接,codereview了 ...

  6. oracle数据库ID自增长

    使用sequence --创建sequenceCREATE SEQUENCE emp_sequence      INCREMENT BY 1  -- 每次加几个      START WITH 1  ...

  7. css实现超连接按钮形式显示

    <html> <head> <meta http-equiv="Content-Type" content="text/html; char ...

  8. Oracle字符函数(转换大小写,替换等)

    在oracle中,有一些字符函数: upper(字符串):转换为大写lower(字符串):转换为小写initcap(字符串):首字母大写replace(字符串1,字符串2,字符串3):将串1中所有的串 ...

  9. js 浮点数加减问题

      /**  ** 加法函数,用来得到精确的加法结果  ** 说明:javascript的加法结果会有误差,在两个浮点数相加的时候会比较明显.这个函数返回较为精确的加法结果.  ** 调用:accAd ...

  10. iOS开发:详解Objective-C runTime

    Objective-C总Runtime的那点事儿(一)消息机制 最近在找工作,Objective-C中的Runtime是经常被问到的一个问题,几乎是面试大公司必问的一个问题.当然还有一些其他问题也几乎 ...