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

  1. Markdown 语法速查表
  2. 1 标题与文字格式
  3. 标题
  4. # 这是 H1 <一级标题>
  5. ## 这是 H2 <二级标题>
  6. ###### 这是 H6 <六级标题>
  7. 文字格式
  8. **这是文字粗体格式**
  9. *这是文字斜体格式*
  10. ~~在文字上添加删除线~~
  11. 2 列表
  12. 无序列表
  13. * 项目1
  14. * 项目2
  15. * 项目3
  16. 有序列表
  17. 1. 项目1
  18. 2. 项目2
  19. 3. 项目3
  20. * 项目1
  21. * 项目2
  22. 3 其它
  23. 图片
  24. ![图片名称](http://gitcafe.com/image.png)
  25. 链接
  26. [链接名称](http://gitcafe.com)
  27. 引用
  28. > 第一行引用文字
  29. > 第二行引用文字
  30. 水平线
  31. ***
  32. 代码
  33. `<hello world>`
  34. 代码块高亮
  35. ```ruby
  36. def add(a, b)
  37. return a + b
  38. end
  39. ```
  40. 表格
  41. 表头  | 表头
  42. ------------- | -------------
  43. 单元格内容  | 单元格内容
  44. 单元格内容l  | 单元格内容

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

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

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


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

  1. # shurnim-storage
  2. ![Shurnim icon](http://onecoder.qiniudn.com/8wuliao/DLPii2Jx/rEBO.jpg)
  3. ## 目录
  4. * [背景介绍](#背景介绍)
  5. * [项目介绍](#项目介绍)
  6. * [使用说明](#使用说明)
  7. * [获取代码](#获取代码)
  8. * [开发插件](#开发插件)
  9. * [使用ShurnimStorage接口](#使用ShurnimStorage接口)
  10. * [接口介绍](#接口介绍)
  11. * [使用样例](#使用样例)
  12. * [其他](#其他)
  13. <a name="背景介绍"></a>
  14. ## 背景介绍
  15. *Shurnim*,是我和我老婆曾经养过的一只仓鼠的名字。<br/>
  16. *shurnim-storage*,是一个插件式云存储/网盘同步管理工具。是在参加又拍云开发大赛的过程中设计并开发。
  17. <a name="项目介绍"></a>
  18. ## 项目介绍
  19. *shurnim-storage* 的设计初衷是给大家提供一个可方便扩展的云存储/网盘同步工具。分后端接口和前端UI界面两部分。<br>
  20. 由于目前各种云存储和网盘系统层出不穷,单一工具往往支持支持某几个特定存储之间的同步,如**又拍云**到**七牛云存储**的同步工具,此时如若想同步到其他存则可能需要新的工具,给用户带来不便。*shurnim-storage*  正是为了解决此问题而设计的。
  21. 在*shurnim-storage*中,用户使用的固定的统一的后端接口。而所有云存储/网盘API的支持则是以插件的形式部署到系统中的。如此,如果用户想要一个从**又拍云**到**Dropbox**的同步工具,则只需要在原有基础上,增加**Dropbox**的插件,即可实现互通,方便快捷。<br/>
  22. 同时,后端统一接口的设计也考虑到界面开发的需求,可直接通过后端提供的接口开发具有上述扩展功能的云存储UI工具。<br>
  23. 目前,后端整体框架的核心部分已经基本开发完成。只需逐步补充后端接口和插件开发接口的定义即可。但由于个人时间和能力所限,UI部分没有开发,有兴趣的同学可以一试。
  24. <a name="使用说明"></a>
  25. ## 使用说明
  26. <a name="获取代码"></a>
  27. ### 获取代码
  28. * gitcafe项目主页: <https://gitcafe.com/onecoder/shurnim-storage-for-UPYUN>
  29. * OSChina项目主页: <http://git.oschina.net/onecoder/shurnim-storage><br>
  30. OSChina上的会持续更新。
  31. 另外你也可以通过OSChina的Maven库获取依赖,或者自己编译jar包。
  32. * maven
  33. 1. 加入OSC仓库
  34. <repositories>
  35. <repository>
  36. <id>nexus</id>
  37. <name>local private nexus</name>
  38. <url>http://maven.oschina.net/content/groups/public/</url>
  39. <releases>
  40. <enabled>true</enabled>
  41. </releases>
  42. <snapshots>
  43. <enabled>false</enabled>
  44. </snapshots>
  45. </repository>
  46. </repositories>
  47. 2. 加入依赖
  48. <dependency>
  49. <groupId>com.coderli</groupId>
  50. <artifactId>shurnim-storage</artifactId>
  51. <version>0.1-alpha</version>
  52. </dependency>
  53. * Gradle 编译Jar
  54. 在项目目录执行
  55. gradle jar
  56. <a name="开发插件"></a>
  57. ### 开发插件
  58. 在*shurnim-storage*中,插件就像一块一块的积木,不但支撑着框架的功能,也是框架可扩展性的基石。开发一个插件,仅需两步:
  59. 1. 实现PluginAPI接口
  60. ```
  61. package com.coderli.shurnim.storage.plugin;
  62. import java.io.File;
  63. import java.util.List;
  64. import com.coderli.shurnim.storage.plugin.model.Resource;
  65. /**
  66. * 各种云存储插件需要实现的通用接口
  67. *
  68. * @author OneCoder
  69. * @date 2014年4月22日 下午9:43:41
  70. * @website http://www.coderli.com
  71. */
  72. public interface PluginAPI {
  73. /**
  74. * 初始化接口
  75. *
  76. * @author OneCoder
  77. * @date 2014年5月19日 下午10:47:40
  78. */
  79. void init();
  80. /**
  81. * 获取子资源列表
  82. *
  83. * @param parentPath
  84. * @return
  85. * @author OneCoder
  86. * @date 2014年4月24日 下午11:29:14
  87. */
  88. List<Resource> getChildResources(String parentPath);
  89. /**
  90. * 下载特定的资源
  91. *
  92. * @param parentPath
  93. *            目录路径
  94. * @param name
  95. *            资源名称
  96. * @param storePath
  97. *            下载资源保存路径
  98. * @return
  99. * @author OneCoder
  100. * @date 2014年4月24日 下午11:30:19
  101. */
  102. Resource downloadResource(String parentPath, String name, String storePath);
  103. /**
  104. * 创建文件夹
  105. *
  106. * @param path
  107. *            文件夹路径
  108. * @param auto
  109. *            是否自动创建父目录
  110. * @return
  111. * @author OneCoder
  112. * @date 2014年5月15日 下午10:10:04
  113. */
  114. boolean mkdir(String path, boolean auto);
  115. /**
  116. * 上传资源
  117. *
  118. * @param parentPath
  119. *            父目录路径
  120. * @param name
  121. *            资源名称
  122. * @param uploadFile
  123. *            待上传的本地文件
  124. * @return
  125. * @author OneCoder
  126. * @date 2014年5月15日 下午10:40:13
  127. */
  128. boolean uploadResource(String parentPath, String name, File uploadFile);
  129. }
  130. ```
  131. 目前插件的接口列表仅为同步资源设计,如果想要支持更多操作(如删除,查找等),可扩展该接口定义。<br/><br/>
  132. 接口中,所有的参数和返回值均为*shurnim-storage*框架中定义的通用模型。因此,您在开发插件过程中需要将特定SDK中的模型转换成接口中提供的模型。<br/><br/>
  133. 插件实现类只要与*shurnim-storage*工程在同一个classpath即可使用。您既可以直接在源码工程中开发插件,就如工程里提供的*upyun*和*qiniu*插件一样,也可以作为独立工程开发,打成jar,放置在同一个classpath下。<br/><br/>
  134. *upyun*插件样例(功能不完整):
  135. ```
  136. package com.coderli.shurnim.storage.upyun.plugin;
  137. import java.io.File;
  138. import java.util.List;
  139. import com.coderli.shurnim.storage.plugin.AbstractPluginAPI;
  140. import com.coderli.shurnim.storage.plugin.model.Resource;
  141. import com.coderli.shurnim.storage.plugin.model.Resource.Type;
  142. import com.coderli.shurnim.storage.upyun.api.UpYun;
  143. public class UpYunPlugin extends AbstractPluginAPI {
  144. private UpYun upyun;
  145. private String username;
  146. private String password;
  147. private String bucketName;
  148. public UpYun getUpyun() {
  149. return upyun;
  150. }
  151. public void setUpyun(UpYun upyun) {
  152. this.upyun = upyun;
  153. }
  154. public String getUsername() {
  155. return username;
  156. }
  157. public void setUsername(String username) {
  158. this.username = username;
  159. }
  160. public String getPassword() {
  161. return password;
  162. }
  163. public void setPassword(String password) {
  164. this.password = password;
  165. }
  166. public String getBucketName() {
  167. return bucketName;
  168. }
  169. public void setBucketName(String bucketName) {
  170. this.bucketName = bucketName;
  171. }
  172. /*
  173. * (non-Javadoc)
  174. *
  175. * @see
  176. * com.coderli.shurnim.storage.plugin.PluginAPI#getChildResources(java.lang
  177. * .String)
  178. */
  179. @Override
  180. public List<Resource> getChildResources(String parentPath) {
  181. return null;
  182. }
  183. /*
  184. * (non-Javadoc)
  185. *
  186. * @see
  187. * com.coderli.shurnim.storage.plugin.PluginAPI#downloadResource(java.lang
  188. * .String, java.lang.String, java.lang.String)
  189. */
  190. @Override
  191. public Resource downloadResource(String parentPath, String name,
  192. String storePath) {
  193. File storeFile = new File(storePath);
  194. //          if (!storeFile.exists()) {
  195. //               try {
  196. //                    storeFile.createNewFile();
  197. //               } catch (IOException e) {
  198. //                    e.printStackTrace();
  199. //               }
  200. //          }
  201. String filePath = getFullPath(parentPath, name);
  202. upyun.readDir("/api");
  203. if (upyun.readFile(filePath, storeFile)) {
  204. Resource result = new Resource();
  205. result.setName(name);
  206. result.setPath(parentPath);
  207. result.setType(Type.FILE);
  208. result.setLocalFile(storeFile);
  209. return result;
  210. }
  211. return null;
  212. }
  213. String getFullPath(String parentPath, String name) {
  214. if (!parentPath.endsWith(File.separator)) {
  215. parentPath = parentPath + File.separator;
  216. }
  217. return parentPath + name;
  218. }
  219. /*
  220. * (non-Javadoc)
  221. *
  222. * @see com.coderli.shurnim.storage.plugin.PluginAPI#mkdir(java.lang.String,
  223. * boolean)
  224. */
  225. @Override
  226. public boolean mkdir(String path, boolean auto) {
  227. // TODO Auto-generated method stub
  228. return false;
  229. }
  230. /*
  231. * (non-Javadoc)
  232. *
  233. * @see
  234. * com.coderli.shurnim.storage.plugin.PluginAPI#uploadResource(java.lang
  235. * .String, java.lang.String, java.io.File)
  236. */
  237. @Override
  238. public boolean uploadResource(String parentPath, String name,
  239. File uploadFile) {
  240. // TODO Auto-generated method stub
  241. return false;
  242. }
  243. /*
  244. * (non-Javadoc)
  245. *
  246. * @see com.coderli.shurnim.storage.plugin.AbstractPluginAPI#init()
  247. */
  248. @Override
  249. public void init() {
  250. upyun = new UpYun(bucketName, username, password);
  251. }
  252. }
  253. ```
  254. 2. 编写插件配置文件
  255. ```
  256. <?xml version="1.0" encoding="UTF-8"?>
  257. <plugin>
  258. <id>qiniu</id>
  259. <name>七牛云存储</name>
  260. <api>
  261. <className>com.coderli.shurnim.storage.qiniu.QiniuPlugin</className>
  262. <params>
  263. <param name="access_key" displayName="ACCESS_KEY">EjREKHI_GFXbQzyrKdVhhXrIRyj3fRC1s9UmZPZO
  264. </param>
  265. <param name="secret_key" displayName="SECRET_KEY">88NofFWUvkfJ6T6rGRxlDSZOQxWkIxY2IsFIXJLX
  266. </param>
  267. <param name="bucketName" displayName="空间名">onecoder
  268. </param>
  269. </params>
  270. </api>
  271. </plugin>
  272. ```
  273. * **id** 为该插件在*shurnim-storage*框架下的唯一标识,不可重复,必填。
  274. * **name** 为显示值,为UI开发提供可供显示的有语义的值。
  275. * **className** 为插件接口实现类的完整路径。必填
  276. * **params/param** 为插件需要用户配置的参数列表。其中
  277. * *name* 代表参数名,需要与接口实现类中的参数名严格一致,且必须有相应的set方法的格式要求严格,即set+首字母大写的参数名。例如:setAccess_key(String arg); 目前只支持*String*类型的参数。
  278. * *displayName* 为参数显示名,同样是为了UI开发的考虑,方便用户开发出可根据参数列表动态显示的UI界面。
  279. * 参数的值可以直接配置在配置文件中,也可以在运行期动态赋值。直接配置值,对于直接使用后端接口来说较为方便。对于UI开发来说,运行期动态赋值更为合理。<br/></br>
  280. 在使用源码工程时,插件配置文件统一放置在工程的*plugins*目录下。你也可以统一放置在任何位置。此时,在构造后端接口实例时,需要告知接口该位置。
  281. <a name="使用ShurnimStorage接口"></a>
  282. ### 使用*ShurnimStorage*接口
  283. <a name="接口介绍"></a>
  284. #### 接口介绍
  285. **ShurnimStorage**接口是*shurinm-storage*框架全局的也是唯一的接口,目前定义如
  286. ```
  287. package com.coderli.shurnim.storage;
  288. import java.util.List;
  289. import java.util.Map;
  290. import com.coderli.shurnim.storage.plugin.model.Plugin;
  291. import com.coderli.shurnim.storage.plugin.model.Resource;
  292. /**
  293. * 后台模块的全局接口<br>
  294. * 通过该接口使用后台的全部功能。<br>
  295. * 使用方式:<br>
  296. * <li>
  297. * 1.先通过{@link #getSupportedPlugins()}方法获取所有支持的平台/插件列表。 <li>
  298. * 2.将列表中返回的ID传入对应的接口参数中,进行对应的平台的相关操作。<br>
  299. * 需要注意的是,不同平台的插件需要给不同的参数赋值,该值可以直接配置在配置文件中。<br>
  300. * 也可以在运行期动态赋值。(会覆盖配置文件中的值。)<br>
  301. *
  302. * 参数列表的设计,方便UI开发人员动态的根据参数列表生成可填写的控件。并给参数赋值。增强了可扩展性。
  303. *
  304. * @author OneCoder
  305. * @date 2014年4月22日 下午9:21:58
  306. * @website http://www.coderli.com
  307. */
  308. public interface ShurnimStorage {
  309. /**
  310. * 获取当前支持的插件列表<br>
  311. * 没有支持的插件的时候可能返回null
  312. *
  313. * @return
  314. * @author OneCoder
  315. * @date 2014年5月7日 下午8:53:25
  316. */
  317. List<Plugin> getSupportedPlugins();
  318. /**
  319. * 给指定的插件的对应参数赋值<br>
  320. * 此处赋值会覆盖配置文件中的默认值
  321. *
  322. * @param pluginId
  323. *            插件ID
  324. * @param paramsKV
  325. *            参数键值对
  326. * @author OneCoder
  327. * @date 2014年5月9日 上午12:41:53
  328. */
  329. void setParamValues(String pluginId, Map<String, String> paramsKV);
  330. /**
  331. * 获取插件对应目录下的资源列表
  332. *
  333. * @param pluginId
  334. *            插件ID
  335. * @param path
  336. *            指定路径
  337. * @return
  338. * @author OneCoder
  339. * @date 2014年5月11日 上午8:52:00
  340. */
  341. List<Resource> getResources(String pluginId, String path);
  342. /**
  343. * 同步资源
  344. *
  345. * @param fromPluginId
  346. *            待同步的插件Id
  347. * @param toPluginIds
  348. *            目标插件Id
  349. * @param resource
  350. *            待同步的资源
  351. * @return 同步结果
  352. * @author OneCoder
  353. * @date 2014年5月11日 上午11:41:24
  354. */
  355. boolean sycnResource(String fromPluginId, String toPluginId,
  356. Resource resource) throws Exception;
  357. }
  358. ```
  359. 当前接口实际仅包含了获取资源列表*getResources*和同步资源*sycnResource*功能,*getSupportedPlugins*和*setParamValues*实际为辅助接口,在UI开发时较为有用。<br/><br/>
  360. 同样,您也可以扩展开发该接口增加更多的您喜欢的特性。例如,同时删除给定存储上的文件。当然,这需要插件接口的配合支持。<br/><br/>
  361. 这里,*sycnResource*设计成插件间一对一的形式,是考虑到获取同步是否成功的结果的需求。如果您想开发一次同步到多个存储的功能,建议您重新开发您自己的接口实现类,因为默认实现会多次下次资源(每次同步后删除),造成网络资源的浪费。
  362. 接口的默认实现类是: **DefaultShurnimStorageImpl**
  363. <a name="使用样例"></a>
  364. #### 使用样例
  365. ```
  366. package com.coderli.shurnim.test.shurnimstorage;
  367. import org.junit.Assert;
  368. import org.junit.BeforeClass;
  369. import org.junit.Test;
  370. import com.coderli.shurnim.storage.DefaultShurnimStorageImpl;
  371. import com.coderli.shurnim.storage.ShurnimStorage;
  372. import com.coderli.shurnim.storage.plugin.model.Resource;
  373. import com.coderli.shurnim.storage.plugin.model.Resource.Type;
  374. /**
  375. * 全局接口测试类<br>
  376. * 时间有限,目前仅作整体接口测试。细粒度的单元测试,随开发补充。
  377. *
  378. * @author OneCoder
  379. * @date 2014年5月19日 下午10:50:27
  380. * @website http://www.coderli.com
  381. */
  382. public class ShurnimStorageTest {
  383. private static ShurnimStorage shurnim;
  384. @BeforeClass
  385. public static void init() {
  386. shurnim = new DefaultShurnimStorageImpl(
  387. "/Users/apple/git/shurnim-storage-for-UPYUN/plugins");
  388. }
  389. @Test
  390. public void testSycnResource() {
  391. Resource syncResource = new Resource();
  392. syncResource.setPath("/api");
  393. syncResource.setName("api.html");
  394. syncResource.setType(Type.FILE);
  395. try {
  396. Assert.assertTrue(shurnim.sycnResource("upyun", "qiniu",
  397. syncResource));
  398. } catch (Exception e) {
  399. e.printStackTrace();
  400. }
  401. }
  402. }
  403. ```
  404. <a name="其他"></a>
  405. ## 其他
  406. 时间仓促,功能简陋,望您包涵。OneCoder(Blog:[http://www.coderli.com](http://www.coderli.com))特别希望看到该项目对您哪怕一点点的帮助。任意的意见和建议,欢迎随意与我沟通,联系方式:
  407. * Email: <wushikezuo@gmail.com>
  408. * QQ:57959968
  409. * Blog:[OneCoder](http://www.coderli.com)
  410. 项目的Bug和改进点,可在OSChina上以issue的方式直接提交给我。

效果预览:

原文:http://www.coderli.com/write-readme-for-your-project/

最新文章

  1. BZOJ 3626: [LNOI2014]LCA [树链剖分 离线|主席树]
  2. UITabBarController 基本定制
  3. Spring-data-jpa详解,全方位介绍。
  4. selenium3各种报错解决办法
  5. git commit --amend
  6. Scau 8633 回文划分 mancher + dp
  7. 移动端开发——javascript
  8. Objective C笔记(第一天)
  9. JS生成GUID算法
  10. MVC设计模式下实现数据库的连接,并获取所有数据到浏览器页面上显示
  11. BackTrack5 (BT5)无线password破解教程之WPA/WPA2-PSK型无线password破解
  12. HDU 4544 湫湫系列故事――消灭兔子
  13. 自动化构建工具gulp简单介绍及使用
  14. Web开发安全小贴士
  15. CoreML试水--图片识别
  16. Google帝国研究——Google的产业构成
  17. ssm学习(五)--加入分页插件
  18. React Native 4 for Android源码分析 一《JNI智能指针之介绍篇》
  19. 缓存session,cookie,sessionStorage,localStorage的区别
  20. cxf 介绍

热门文章

  1. 基于Oracle的SQL优化(崔华著)-整理笔记-第2章“Oracle里的执行计划”
  2. HDU 4686 Arc of Dream 矩阵快速幂,线性同余 难度:1
  3. 一次SQLServer索引损坏问题的排查与修复
  4. 【LeetCode 144_二叉树_遍历】Binary Tree Preorder Traversal
  5. 正则,String中用法,Pattern Matcher
  6. .net 应用程序 发布上线注意事项
  7. HTML5和XHTML的区别
  8. 第8课 goto和void分析
  9. 這是我既C語言作業寫博客後寫的第一篇博客
  10. arcotg_udc: exports duplicate symbol imx_usb_create_charger (owned by kernel)