使用React上载,操纵图像

zhenghuan 8年前
   <p>下面是由 <a href="/misc/goto?guid=4958987272576770953" rel="nofollow,noindex">Damon Bauer</a> 发的一篇帖子,他做着一份很常见的web开发工作: 给用户提供图片上传的方法. 很难说这件事比较简单, 但是在一些强大的工具帮助下也可以完成这样的工作, 而且比之前更加容易. demon甚至全部在浏览器中完成这项工作 实例 <a href="/misc/goto?guid=4959677097643763425" rel="nofollow,noindex">链接</a> ! web开发者常做的一件事情就是提供用户上传图片的解决方案,看上去微不足道的,但是当构建一个上传组件的时候 需要考虑以下几点:</p>    <ul>     <li> <p>你允许用户上传什么格式的图片?</p> </li>     <li> <p>图片需要保证多大? 这将如何影响性能?</p> </li>     <li> <p>图片应该是什么样的长宽比?</p> </li>     <li> <p>图像将如何组织?不合适的图像如何被捕获?</p> </li>     <li> <p>图片将托管在哪里?如何进行管理?</p> </li>    </ul>    <p>服务端工具 如 <a href="/misc/goto?guid=4958523256111367769" rel="nofollow,noindex">Paperclip</a> 和 <a href="/misc/goto?guid=4959677097766837282" rel="nofollow,noindex">ImageProcessor</a> 为以上考虑提供了解决方案. 不幸的是,在单页应用中没有一个现成的工具可以使用 (我发现). 在这里我将会告诉你如何在 <a href="/misc/goto?guid=4958968605084997344" rel="nofollow,noindex">React</a> 应用中不使用后端语言实现它.</p>    <p>下面是我们将会构建的示例图:</p>    <p><img src="https://simg.open-open.com/show/5df37fa089701821de9df4382735e12a.gif"></p>    <h3>工具包</h3>    <p>我用到了以下这些工具:</p>    <ul>     <li> <p><a href="/misc/goto?guid=4959677097877044844" rel="nofollow,noindex">react-dropzone</a> 从一个用户那里获取图片</p> </li>     <li> <p><a href="/misc/goto?guid=4959615056526057439" rel="nofollow,noindex">superagent</a> 为了转移上传的图片</p> </li>     <li> <p><a href="/misc/goto?guid=4959651274428824296" rel="nofollow,noindex">Cloudinary</a> 存储和操作图片</p> </li>    </ul>    <h3>建立一个 Cloudinary</h3>    <p><a href="/misc/goto?guid=4958853421776826610" rel="nofollow,noindex">Cloudinary</a> 是一个基于云的服务,您可以存储,处理,管理和提供图像。我选择使用Cloudinary,因为它有一个免费的版本,包括我需要的所有功能。你至少需要一个免费帐户上手。</p>    <p>比方说,你要裁切,重新调整和为上传的图片加一个筛选器。 Cloudinary有 <em>transformations</em> 的概念,就是它需要和你需要修改的图像链接在一起。上传后,转换就会发生,修改和保存新的图片。</p>    <p>在 Cloudinary 控制台, 选择 <strong>Settings > Upload</strong> 之后 选择 "Add upload preset" 在上传预置项中.</p>    <p><img src="https://simg.open-open.com/show/2c6dacd883dc9192a7575a5505ab7526.png"></p>    <p>在下面的屏幕,修改“模式”为“无签名”。这是必要的,这样你可以不使用服务器端语言协商的私钥上传到Cloudinary。</p>    <p><img src="https://simg.open-open.com/show/85648bef4b1ba8844f87a46e8c551029.png"></p>    <p>通过在“传入转换”中选择“编辑”添加你想要的转换。在这里,您可以裁剪,调整大小,质量的变化,旋转,过滤等保存预设,就是这样!现在,您可以上传,处理,存储和图像服务为您的应用程序。注意 <em>preset</em> ,我们将在以后使用它。现在让我们继续前进的代码。</p>    <h3>获取用户输入</h3>    <p>为了处理图像上传, 我使用了 <a href="/misc/goto?guid=4959677097877044844" rel="nofollow,noindex">react-dropzone</a> . 它包括的功能,如拖放,文件类型限制,多文件上传。</p>    <p>首先,安装的依赖关系。在命令行中运行:</p>    <pre>  <code class="language-javascript">npm install react react-dropzone superagent --save`</code></pre>    <p>然后在你组件中 引入 React , react-dropzone , 和 superagent . 我使用 the ES6 import 语法:</p>    <pre>  <code class="language-javascript">import React from 'react';  import Dropzone from 'react-dropzone';  import request from 'superagent';</code></pre>    <p>我们之后会使用 superagent . 但现在, 在你组件的渲染方法中, 包括一个 react-dropzone 实例:</p>    <pre>  <code class="language-javascript">export default class ContactForm extends React.Component {      render() {      <Dropzone        multiple={false}        accept="image/*"        onDrop={this.onImageDrop.bind(this)}>        <p>Drop an image or click to select a file to upload.</p>      </Dropzone>    }</code></pre>    <p>下面是组件运行的要点:</p>    <ul>     <li> <p>multiple={false} 允许用户只能一次性上传一张图片.</p> </li>     <li> <p>accept="image/*" 允许任何种类的图片.你可以更明确的限制只有特定的文件类型, e.g. accept="image/jpg,image/png" .</p> </li>     <li> <p>onDrop 当图像被上传的触发方法.</p> </li>    </ul>    <p>当时有 React ES5 类 语法 ( React.createClass ), 所有方法都是 "自动绑定" 到类实例. 这篇文章代码使用ES6类型语法 ( extends React.Component ), 不提供自动绑定. 这是我们为什么使用 .bind(this) 在 onDrop 属性. (如果你不熟悉 .bind , 你可以 <a href="/misc/goto?guid=4958833744979719710" rel="nofollow,noindex">read about it here</a> .</p>    <h3>处理图像拖放</h3>    <p>现在,让我们设置一个图像上传之后的方法。</p>    <p>首先, 设置一个 const 包括上传重要的两条信息:</p>    <ol>     <li> <p>上传预设ID(在创建上传预设自动创建)</p> </li>     <li> <p>你的 Cloudinary 上传 URL</p> </li>    </ol>    <pre>  <code class="language-javascript">// import statements    const CLOUDINARY_UPLOAD_PRESET = 'your_upload_preset_id';  const CLOUDINARY_UPLOAD_URL = 'https://api.cloudinary.com/v1_1/your_cloudinary_app_name/upload';    export default class ContactForm extends React.Component {  // render()</code></pre>    <p>接下来,添加组件初始化状态入口(using this.setState );我称之为 UploadedFileCloudinaryUrl 。最终,这将持有Cloudinary创建上传的图片的URL。稍后,我们将使用这一块的状态。</p>    <pre>  <code class="language-javascript">export default class ContactForm extends React.Component {      constructor(props) {      super(props);        this.state = {        uploadedFileCloudinaryUrl: ''      };    }</code></pre>    <p>react-dropzone 文档状态 总是会 返回一个上传文件的数组, 之后我们会传递参数到 files 参数 在 onImageDrop 方法. 只要我们允许一次只能传递一张图片, 我们知道,在图像始终处于所述数组的第一位置.</p>    <p>Call handleImageUpload , 传递图片 ( files[0] ) 到这个方法. 我把它分成几个独立的方法, 遵从 <a href="/misc/goto?guid=4958987564349588981" rel="nofollow,noindex">Single responsibility principle</a> 的规则. 重要地, 这个原则教你的方法保持紧凑,只做一件事。</p>    <pre>  <code class="language-javascript">export default class ContactForm extends React.Component {      constructor(props) { ... }      onImageDrop(files) {      this.setState({        uploadedFile: files[0]      });        this.handleImageUpload(files[0]);    }      render() { ... }    }</code></pre>    <h3>处理图像上载和传送</h3>    <p>首先, 使用 superagent 上传到 Cloudinary 使用我们之前设置的俩个 const . 使用 <a href="/misc/goto?guid=4959677098103630008" rel="nofollow,noindex"> .field method </a> 可以对POST请求绑定数据.这个类型的数据包括所有的Cloudinary 处理图片的信息. 通过调用 .end , 请求被执行 并且会提供一个回调.</p>    <pre>  <code class="language-javascript">export default class ContactForm extends React.Component {      constructor(props) { ... }      onImageDrop(files) { ... }      handleImageUpload(file) {      let upload = request.post(CLOUDINARY_UPLOAD_URL)                          .field('upload_preset', CLOUDINARY_UPLOAD_PRESET)                          .field('file', file);        upload.end((err, response) => {        if (err) {          console.error(err);        }          if (response.body.secure_url !== '') {          this.setState({            uploadedFileCloudinaryUrl: response.body.secure_url          });        }      });    }      render() { ... }    }</code></pre>    <p>在 .end 回调里,我记录返回的任何错误。同时这也可能是最好的方法告诉大家,发生了错误。</p>    <p>下一步,我们检查我们是否收到包含一个URL的响应的非空字符串。如果答案是肯定的,那么图像上载和操纵和Cloudinary产生一个URL。例如,如果一个用户编辑他们的个人资料并上传的图片,您可以从Cloudinary获得新的图像URL并存储在数据库中。</p>    <p>因此到目前为止,我已经写的代码,用户可以删除一个图像 并且组件将其发送到Cloudinary之后提供变换的图像URL让我们使用。</p>    <h3>继续渲染</h3>    <p>组件的最后一部分是一个 div 预览上传的图片。</p>    <pre>  <code class="language-javascript">export default class ContactForm extends React.Component {      constructor(props) { ... }      onImageDrop(files) { ... }      handleImageUpload(file) { ... }      render() {      <div>        <div className="FileUpload">          ...        </div>          <div>          {this.state.uploadedFileCloudinaryUrl === '' ? null :          <div>            <p>{this.state.uploadedFile.name}</p>            <img src={this.state.uploadedFileCloudinaryUrl} />          </div>}        </div>      </div>    }</code></pre>    <p>三元运算符输出 null (nothing) 如果 uploadedFileCloudinaryUrl 状态是一个空的字符串.默认重新调用, 我们设置组件的 uploadedFileCloudinaryUrl 状态为一个空的字符串; 这意味着当组件渲染了, 这个 div 将会为空.</p>    <p>然而, 当 Cloudinary 回应一个 URL, 状态不再是一个空的URL, 因为我们在 handleImageUpload 中更新了状态. 这时, 组件将会重新渲染, 显示上传文件的名称 并且展示修改后的图像.</p>    <h3>打包</h3>    <p>这仅仅是一个图像上传组件的基础。还有很多你可以添加的特性,如:</p>    <ul>     <li> <p>允许上传多张图片</p> </li>     <li> <p>删除上传的图片</p> </li>     <li> <p>如果上传由于某种原因失显示错误</p> </li>     <li> <p>使用移动设备的摄像头作为上载源</p> </li>    </ul>    <p>到目前为止,这种设置很适合我的需求。必须硬编码上传的预设是不完美的,但我还没有遇到任何与它相关问题。</p>    <p>幸运地,你已经理解了如何使用React上传,存储,并没有使用的服务器端语言来处理图像。如果您有任何问题或意见,我很乐意听到他们的声音!我创建了一个库,在这里,你可以 <a href="/misc/goto?guid=4959677097643763425" rel="nofollow,noindex">点击这里</a> 看到.</p>    <p> </p>    <p>来自:http://www.zcfy.cc/article/image-upload-and-manipulation-with-react-1114.html</p>    <p> </p>    <p><span style="background:rgb(189, 8, 28) url("data:image/svg+xml; border-radius:2px; border:medium none; color:rgb(255, 255, 255); cursor:pointer; display:none; font:bold 11px/20px "Helvetica Neue",Helvetica,sans-serif; opacity:1; padding:0px 4px 0px 0px; position:absolute; text-align:center; text-indent:20px; width:auto; z-index:8675309">Save</span></p>    <p> </p>