草庐IT

上传时获取图片和视频宽高(onload和Promise配合使用)

T.qq_44859233 2023-10-27 原文

JavaScript如何获取图片和视频的尺寸呢?本文很详细,一步一步来,循序渐进.

方法1:得到图片的src属性,是否可以读到图片的宽高?
方法2:得到图片的DOM元素,是否可以读取图片的宽高?

下面我们一起验证一下吧~

获取图片原始的真实宽高

首先我们考虑入参,图片的src跟图片的DOM,接着我们如何读取宽高,先来看看图片的DOM元素有没有什么属性吧

随便搞张图片测试一下

我们看到有四个属性,width,height, naturalHeight, naturalWidth

那到底使用那种属性更合适呢?看看MDN文档怎么说

naturalWidth和naturalHeight—[(MDN文档)](HTMLImageElement.naturalHeight - Web API 接口参考 | MDN (mozilla.org))

width和height—:图像嵌入元素 - HTML(超文本标记语言) | MDN (mozilla.org)

看完之后,还是选择naturalWidth和naturalHeight更合适些

选择naturalWidth和naturalHeight

然后开始干

function getImgRealSize(img) {
  // img是dom元素
    if (img instanceof HTMLElement) {
      // 拥有真实宽高属性的dom元素
      if (
        img.naturalWidth !== null && img.naturalWidth !== undefined
      ) {
         return {
            naturalWidth: img.naturalWidth,
            naturalHeight: img.naturalHeight
          }
        
      } else {
        // 没有真实宽高属性的dom元素
        ....
      }
    } else {
      // img是图片url字符串,通过创建图片dom获取真实宽高
      ....
    }
}

图片url转换为Image对象

如果是一张图片的url,如何读取,我知道有一个Image对象Image() - Web API 接口参考 | MDN (mozilla.org)

**Image()**函数将会创建一个新的HTMLImageElement实例。

看到这句话,我就知道怎么读取啦~

const fn = imgSrc => {
  if (!(typeof imgSrc === 'string' && !!imgSrc)) {
    return reject(new Error('img url not found'))
  }
  let imgDom = new Image()
  imgDom.src = imgSrc
  console.log(imgDom.naturalWidth, imgDom.naturalHeight);
  console.log(imgDom);
}

咦?怎么回事,脑子快速运转中~~~

图片没有加载好,就已经直接去读取图片的属性啦,所以读到的属性值是0,那为啥console.dir输出的是有值的DOM数据呢?
console.log - Web API 接口参考 | MDN (mozilla.org)

console读取对象的引用

哈哈,console.dir输出的是对象的引用,在控制台输出的时候,已经有DOM值了

那怎么解决呢???

window.onload事件,大家肯定都听过吧,Window - Web API 接口参考 | MDN (mozilla.org)

DOM完全加载完毕(onload)

DOM完全加载完毕,看到这句话,就知道怎么做啦!

当图像装载完毕时调用

const fn = imgSrc => {
  if (!(typeof imgSrc === 'string' && !!imgSrc)) {
    return reject(new Error('img url not found'))
  }
  let imgDom = new Image()
  imgDom.src = imgSrc
  imgDom.onload = () => {
    console.log(imgDom.naturalWidth, imgDom.naturalHeight);
  }
}

哈哈哈,perfect

一顿操作猛如虎,赶紧试试

    const fn = imgSrc => {
      if (!(typeof imgSrc === 'string' && !!imgSrc)) {
        return reject(new Error('img url not found'))
      }
      let imgDom = new Image()
      imgDom.src = imgSrc
      imgDom.onload = () => {
        return {
          naturalWidth: imgDom.naturalWidth,
          naturalHeight: imgDom.naturalHeight
        }
      }
    }

    function getImgRealSize(img) {
      // img是dom元素
      if (img instanceof HTMLElement) {
        // 拥有真实宽高属性的dom元素
        if (
          img.naturalWidth !== null && img.naturalWidth !== undefined
        ) {
          return {
            naturalWidth: img.naturalWidth,
            naturalHeight: img.naturalHeight
          }
        } else {
          // 没有真实宽高属性的dom元素
          fn(img.src)
        }
      } else {
        // img是图片url字符串,通过创建图片dom获取真实宽高
        fn(img)
      }
    }

结果…额…果然没有这么简单

没事,继续优化啦

首先分析一下为什么会是0???
想想刚刚那个Image对象,难道需要加onload事件,让DOM加载完成

加点辅助的输出,看看怎么回事

控制台输出如下:

哦哦,找到原因啦,开心~

onload事件执行需要时间,是个异步执行,导致同步已经返回了undefined,所以怎么办呢???

返回promise

大家知道await和async关键字吧, 那肯定知道await就能够等待事件执行完毕,再继续执行后续操作,其实这就是promise微任务加载啦,可以利用promise的resolve和reject解决

const fn = imgSrc => {
  if (!(typeof imgSrc === 'string' && !!imgSrc)) {
    return reject(new Error('img url not found'))
  }
  let imgDom = new Image()
  imgDom.src = imgSrc
  imgDom.onload = () => {
    return {
      naturalWidth: imgDom.naturalWidth,
      naturalHeight: imgDom.naturalHeight
    }
  }
}
function getImgRealSize(img) {
  return new Promise((resolve, reject) => {
    const rs = { naturalWidth: 0, naturalHeight: 0 }
    // img是dom元素
    if (img instanceof HTMLElement) {
      // 拥有真实宽高属性的dom元素
      if (
        img.naturalWidth !== null && img.naturalWidth !== undefined
      ) {
        img.onload = () => {
          const { naturalWidth, naturalHeight } = img
          Object.assign(rs, { naturalWidth: naturalWidth, naturalHeight: naturalHeight })
          resolve(rs)
        }
      } else {
        // 没有真实宽高属性的dom元素
        fn(img.src)
      }
    } else {
      // img是图片url字符串,通过创建图片dom获取真实宽高
      fn(img)
    }
  })
}
// 既然返回了一个promise,那就需要使用.then接收啦
getImgRealSize(document.querySelector("#imgDom")).then(res => {
  console.log(res, 11)
}) 

这下子就调用成功啦,哈哈

然后看看传入参数是url的时候,会是什么样子吧

没有输出,怎么回事呢,有了上面的经验,大家应该知道了吧,fn函数需要优化,不然就会返回undefined啦,注意fn函数里面需要使用到promise的resolve和reject,所以我们需要把fn写入到promise里面,形成一个闭包环境.

function getImgRealSize(img) {
  return new Promise((resolve, reject) => {
    const rs = { naturalWidth: 0, naturalHeight: 0 }
    const fn = imgSrc => {
      if (!(typeof imgSrc === 'string' && !!imgSrc)) {
        return reject(new Error('img url not found'))
      }
      let imgDom = new Image()
      imgDom.src = imgSrc
      imgDom.onload = () => {
        const { width, height } = imgDom // 如果imgDom存在naturalWidth属性,那么它的值等于width
        Object.assign(rs, { naturalWidth: width, naturalHeight: height })
        imgDom = null;
        resolve(rs)
      }
    }
    // img是dom元素
    if (img instanceof HTMLElement) {
      // 拥有真实宽高属性的dom元素
      if (
        img.naturalWidth !== null && img.naturalWidth !== undefined
      ) {
        img.onload = () => {
          const { naturalWidth, naturalHeight } = img
          Object.assign(rs, { naturalWidth: naturalWidth, naturalHeight: naturalHeight })
          resolve(rs)
        }
      } else {
        // 没有真实宽高属性的dom元素
        fn(img.src)
      }
    } else {
      // img是图片url字符串,通过创建图片dom获取真实宽高
      fn(img)
    }
  })
}
    
getImgRealSize(document.querySelector("#imgDom")).then(res => {
  console.log(res, 11)
})
getImgRealSize("https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/cb510bf31a7b4d3692c22b390f157f32~tplv-k3u1fbpfcp-zoom-mark-crop-v2:0:0:1812:480.awebp?").then(res => {
  console.log(res, 22)
}) 

哈哈,看到返回结果啦,开心到飞起~~~

优化promise,Image的onerror

既然使用了promise,那我们就应该考虑什么时候使用reject呢,所以我们要考虑异常情况哟
我们前面使用到了Image对象的onload事件,但其实除了onload事件,Image也有onerror事件呢?
当图片加载出现异常的时候调用.

所以优化代码如下:

function getImgRealSize(img) {
  return new Promise((resolve, reject) => {
    const rs = { naturalWidth: 0, naturalHeight: 0 }
    const fn = imgSrc => {
      if (!(typeof imgSrc === 'string' && !!imgSrc)) {
        return reject(new Error('img url not found'))
      }
      let imgDom = new Image()
      imgDom.src = imgSrc
      imgDom.onload = () => {
        const { width, height } = imgDom // 如果imgDom存在naturalWidth属性,那么它的值等于width
        Object.assign(rs, { naturalWidth: width, naturalHeight: height })
        imgDom = null;
        resolve(rs)
      }
      imgDom.onerror = (err) => {
        imgDom = null;
        reject(new Error('image onerror'))
      }
    }
    // img是dom元素
    if (img instanceof HTMLElement) {
      // 拥有真实宽高属性的dom元素
      if (
        img.naturalWidth !== null && img.naturalWidth !== undefined
      ) {
        img.onload = () => {
          const { naturalWidth, naturalHeight } = img
          Object.assign(rs, { naturalWidth: naturalWidth, naturalHeight: naturalHeight })
          resolve(rs)
        }
        img.onerror = (err) => {
          reject(err)
        }
      } else {
        // 没有真实宽高属性的dom元素
        fn(img.src)
      }
    } else {
      // img是图片url字符串,通过创建图片dom获取真实宽高
      fn(img)
    }
  })
}

哈哈,这样就可以了,但是既然都已经做到这里了,我们再多考虑一种情况,我需要上传文件的时候判断图片的尺寸,这里演示就使用elemment-ui的上传组件啦, el-upload

上传时获取图片,使用FileReader对象

el-upload上传组件的说明: https://element.eleme.cn/#/zh-CN/component/upload

在before-upload这个钩子这里,就能拿到文件对象啦~~~

开干,冲冲冲

// 上传时读到的文件,将文件转为img的src
readFiles(file) {
  return new Promise((resolve, reject) => {
    let readerFile = new FileReader();
    readerFile.onload = (evt) => {
      getImgRealSize(evt.target.result).then(res => {
        console.log('img的宽高 -->', res.naturalWidth, res.naturalHeight)
        if(res.naturalWidth / res.naturalHeight === 16 / 9) {
          resolve(true);
        } else {
          this.$message.error(`直播间只支持16:9比例文件,请重新选择文件`)
          reject(false)
        }
      });         
    }
    readerFile.onerror = (err) => {
      console.error(err);
      reject(err)
    }
    readerFile.readAsDataURL(file);
  })
},
// 文件上传前格式,大小和数量校验提示
async function beforeAvatarUpload(file) {
  let isPic = file.type === "image/jpg" || file.type === "image/png" || file.type === "image/jpeg";
  if (!isPic) {
    this.$message.error("不支持选择的上传格式,请重新选择上传格式为jpg或png")
    return false;
  } else if (file.size > this.exceedSize) {
    this.$message.error("只允许上传1M以内的文件")
    return false;
  } else if (this.imgfileList.length > this.limitNum) {
    this.$message.error(`文件数量已到限制`)
    return false;
  } else {
    return await this.readFiles(file)
  }
  return false;
}

看到了吗?调用了我们上面的getImgRealSize函数,首先我们拿到的是一个文件对象,如果是一张图片的话,直接是拿不到图片的url的,所以我们需要使用readFiles方法,取出图片的url.

就是我们需要使用FileReader对象去读取文件,然后使用readAsDataURL事件,读取到evt.target.result,这个就是图片的url了.

这里需要注意onload事件哟,相信经过上面的经历,并不需要再提醒了.

优化上传校验

使用了await函数呢,最好使用try…catch包裹一下await函数,因为这样可以捕获异常处理,如果await中的promise出现了问题,也能够及时把问题抛出来,同时又兼容不阻塞当前流程

使用try…catch…包裹await之后,又出现了新的问题

猜猜下面输入一张不是16:9的图片,会出现什么情况?

效果如下:没有拦截成功,还是上传了图片,很明显,走的是catch,但是err没有输出

why?try/catch 能捕获所有异常,并且把reject当成了异常.

知道原因了,那我们是不是可以直接将reject(false)变成resolve(false)呢?

突然发现使用了try…catch…竟然使得程序不正确了,按理说不应该阻塞上传流程了吗?

为此我们再仔细看看element-ui官网(element-ui官网)对el-upload组件的描述

为此写成reject(false)没啥问题,但是加上try…catch…之后就不正常了

这该如何是好???

返回了false,但就是正常上传了,没有停止,所以反方向想想,直接返回false,是不是不能停止上传了

此刻,是不是发现什么问题了.

没关系滴!一番恍然大悟,有async和await的函数,默认变成了promise函数,需要返回Promise.reject才可以停止上传咯!

那显然改了beforeAvatarUpload方法还不够,还需要改readFiles方法,因为这里resolve(false)显然不能停止上传了,我们需要reject(false),不然就得在beforeAvatarUpload方法中继续判断,如果返回false就需要return为Promise.reject()

完整代码

最后完整代码如下:

// 上传时读到的文件,将文件转为img的src
    readFiles(file) {
      return new Promise((resolve, reject) => {
        let readerFile = new FileReader();
        readerFile.onload = (evt) => {
          getImgRealSize(evt.target.result).then(res => {
            console.log('img的宽高 -->', res.naturalWidth, res.naturalHeight)
            if(res.naturalWidth / res.naturalHeight === 16 / 9) {
              resolve(true);
            } else {
              this.$message.error(`直播间只支持16:9比例文件,请重新选择文件`)
              reject(`直播间只支持16:9比例文件,请重新选择文件`)
            }
          });         
        }
        readerFile.onerror = (err) => {
          console.error(err);
          reject(err)
        }
        readerFile.readAsDataURL(file);
      })
    },
    // 文件上传前格式,大小和数量校验提示
    async beforeAvatarUpload(file) {
      let isPic = file.type === "image/jpg" || file.type === "image/png" || file.type === "image/jpeg";
      if (!isPic) {
        this.$message.error("不支持选择的上传格式,请重新选择上传格式为jpg或png")
        return Promise.reject();
      } else if (file.size > this.exceedSize) {
        this.$message.error("只允许上传1M以内的文件")
        return Promise.reject();
      } else if (this.imgfileList.length > this.limitNum) {
        this.$message.error(`文件数量已到限制`)
        return Promise.reject();
      } else {
        try {
          return await this.readFiles(file)
        } catch(err) {
          console.error(err)
          return Promise.reject();
        }
      }
      return Promise.reject();
    },
    
    
可以封装起来,变成公共方法
// 获取图片原始的真实宽高
// img参数:是图片dom元素或者图片url
export function getImgRealSize(img) {
  return new Promise((resolve, reject) => {
    const rs = { naturalWidth: 0, naturalHeight: 0 }
    const fn = imgSrc => {
      if (!(typeof imgSrc === 'string' && !!imgSrc)) {
        return reject(new Error('img url not found'))
      }
      let imgDom = new Image()
      imgDom.src = imgSrc
      imgDom.onload = () => {
        const { width, height } = imgDom // 如果imgDom存在naturalWidth属性,那么它的值等于width
        Object.assign(rs, { naturalWidth: width, naturalHeight: height })
        imgDom = null;
        resolve(rs)
      }
      imgDom.onerror = (err) => {
        imgDom = null;
        reject(new Error('image onerror'))
      }
    }
    // img是dom元素
    if (img instanceof HTMLElement) {
      // 拥有真实宽高属性的dom元素
      if (
        img.naturalWidth !== null && img.naturalWidth !== undefined
      ) {
        img.onload = () => {
          const { naturalWidth, naturalHeight } = img
          Object.assign(rs, { naturalWidth: naturalWidth, naturalHeight: naturalHeight })
          resolve(rs)
        }
        img.onerror = (err) => {
          reject(err)
        }
      } else {
        // 没有真实宽高属性的dom元素
        fn(img.src)
      }
    } else {
      // img是图片url字符串,通过创建图片dom获取真实宽高
      fn(img)
    }
  })
}

上传时获取视频宽高比例

16/9 = 1.777777777…

// 判断视频宽高比例是否为16比9
export function getVideoRealSize(video) {
  return new Promise((resolve, reject) => {
    const url = URL.createObjectURL(file)
    let video = document.createElement('video')
    video.onloadedmetadata = evt => {
      // Revoke when you don't need the url any more to release any reference
      URL.revokeObjectURL(url)
      console.log('video的宽高 -->', video.videoWidth, video.videoHeight)
      if(video.videoWidth / video.videoHeight === 16 / 9) {
          video = null;
          resolve(true);
        } else {
          video = null;
          this.$message.error(`图片比例需要为16 比 9`)
          reject(false)
        }
    }
    video.onerror = (err) => {
      console.error(err);
      video = null;
      reject(err)
    }
    video.src = url
    video.load() // fetches metadata
  })
}

总结

注意:

  1. 在有onload事件的函数中,有返回值,需要使用promise,借助reslove和reject,不然会先返回undefined,没有等到onload事件执行完成,就已经返回了值。
  2. DOM元素需要等待onload事件之后再去读取元素的值,比如元素的naturalWidth和width,不然会先读到0.
  3. 创建DOM元素之后,不需要再使用了就清空DOM元素哟~
  4. 使用了async和await关键字,最好使用try…catch…或.catch()捕获错误

try/catch和 .catch的区别

  1. .catch和try/catch都能捕获异步方法中reject错误,也能捕获其他错误
  2. 但是try/catch需要在async…await…函数中,不然会报错Use node --trace-warnings ... to show where the warning was created,这个报错会导致在catch中的输出阻塞,但是catch中的错误还是会抛出来。
  3. try/catch当捕获到第一个promise出现reject,或者出现异常时,会直接阻塞下面函数的执行与调用,当调用了fn1和fn2时,fn1出现了异常,fn2不会调用了
  4. 上面一种情况, .catch还会继续调用fn2,不会阻塞执行.
  5. 所以如果想要尽管出错了,也还要继续执行下面函数和判断,就使用.catch, 如果希望第一个promise出现异常,下面所有执行都不用了,就使用try…catch…
// 捕获异步中的错误1
const asyncFn1 = async () => {
  try {
      let result1 = await fn(false, 'hello')
      console.log('中间内容输出')
      let result2 = await fn(false, 'world')
      console.log('result1' + result1)
      console.log('result2' + result2)
  } catch (error) {
      console.log('catch:' + error) // 执行
  }
}
asyncFn1();
// catch:fail:hello

/*
// 捕获异步中的错误2
const asyncFn2 = async () => {
  try {
      await fn(false, 'hello').then(() => {}).catch(err => {
          console.log('result1:' + err)
      })
      console.log('中间内容输出')
      await fn(false, 'world').then(() => { }).catch(err => {
          console.log('result2:' + err)
      })
  } catch (error) {
      console.log('catch:' + error)
  }
}
asyncFn2();
result1:fail:hello
中间内容输出
result2:fail:world
*/

有关上传时获取图片和视频宽高(onload和Promise配合使用)的更多相关文章

  1. ruby - 如何使用 Nokogiri 的 xpath 和 at_xpath 方法 - 2

    我正在学习如何使用Nokogiri,根据这段代码我遇到了一些问题:require'rubygems'require'mechanize'post_agent=WWW::Mechanize.newpost_page=post_agent.get('http://www.vbulletin.org/forum/showthread.php?t=230708')puts"\nabsolutepathwithtbodygivesnil"putspost_page.parser.xpath('/html/body/div/div/div/div/div/table/tbody/tr/td/div

  2. ruby - 使用 RubyZip 生成 ZIP 文件时设置压缩级别 - 2

    我有一个Ruby程序,它使用rubyzip压缩XML文件的目录树。gem。我的问题是文件开始变得很重,我想提高压缩级别,因为压缩时间不是问题。我在rubyzipdocumentation中找不到一种为创建的ZIP文件指定压缩级别的方法。有人知道如何更改此设置吗?是否有另一个允许指定压缩级别的Ruby库? 最佳答案 这是我通过查看ruby​​zip内部创建的代码。level=Zlib::BEST_COMPRESSIONZip::ZipOutputStream.open(zip_file)do|zip|Dir.glob("**/*")d

  3. ruby - 为什么我可以在 Ruby 中使用 Object#send 访问私有(private)/ protected 方法? - 2

    类classAprivatedeffooputs:fooendpublicdefbarputs:barendprivatedefzimputs:zimendprotecteddefdibputs:dibendendA的实例a=A.new测试a.foorescueputs:faila.barrescueputs:faila.zimrescueputs:faila.dibrescueputs:faila.gazrescueputs:fail测试输出failbarfailfailfail.发送测试[:foo,:bar,:zim,:dib,:gaz].each{|m|a.send(m)resc

  4. ruby-on-rails - 使用 Ruby on Rails 进行自动化测试 - 最佳实践 - 2

    很好奇,就使用ruby​​onrails自动化单元测试而言,你们正在做什么?您是否创建了一个脚本来在cron中运行rake作业并将结果邮寄给您?git中的预提交Hook?只是手动调用?我完全理解测试,但想知道在错误发生之前捕获错误的最佳实践是什么。让我们理所当然地认为测试本身是完美无缺的,并且可以正常工作。下一步是什么以确保他们在正确的时间将可能有害的结果传达给您? 最佳答案 不确定您到底想听什么,但是有几个级别的自动代码库控制:在处理某项功能时,您可以使用类似autotest的内容获得关于哪些有效,哪些无效的即时反馈。要确保您的提

  5. ruby - 在 Ruby 中使用匿名模块 - 2

    假设我做了一个模块如下:m=Module.newdoclassCendend三个问题:除了对m的引用之外,还有什么方法可以访问C和m中的其他内容?我可以在创建匿名模块后为其命名吗(就像我输入“module...”一样)?如何在使用完匿名模块后将其删除,使其定义的常量不再存在? 最佳答案 三个答案:是的,使用ObjectSpace.此代码使c引用你的类(class)C不引用m:c=nilObjectSpace.each_object{|obj|c=objif(Class===objandobj.name=~/::C$/)}当然这取决于

  6. ruby - 使用 ruby​​ 和 savon 的 SOAP 服务 - 2

    我正在尝试使用ruby​​和Savon来使用网络服务。测试服务为http://www.webservicex.net/WS/WSDetails.aspx?WSID=9&CATID=2require'rubygems'require'savon'client=Savon::Client.new"http://www.webservicex.net/stockquote.asmx?WSDL"client.get_quotedo|soap|soap.body={:symbol=>"AAPL"}end返回SOAP异常。检查soap信封,在我看来soap请求没有正确的命名空间。任何人都可以建议我

  7. python - 如何使用 Ruby 或 Python 创建一系列高音调和低音调的蜂鸣声? - 2

    关闭。这个问题是opinion-based.它目前不接受答案。想要改进这个问题?更新问题,以便editingthispost可以用事实和引用来回答它.关闭4年前。Improvethisquestion我想在固定时间创建一系列低音和高音调的哔哔声。例如:在150毫秒时发出高音调的蜂鸣声在151毫秒时发出低音调的蜂鸣声200毫秒时发出低音调的蜂鸣声250毫秒的高音调蜂鸣声有没有办法在Ruby或Python中做到这一点?我真的不在乎输出编码是什么(.wav、.mp3、.ogg等等),但我确实想创建一个输出文件。

  8. ruby-on-rails - 'compass watch' 是如何工作的/它是如何与 rails 一起使用的 - 2

    我在我的项目目录中完成了compasscreate.和compassinitrails。几个问题:我已将我的.sass文件放在public/stylesheets中。这是放置它们的正确位置吗?当我运行compasswatch时,它不会自动编译这些.sass文件。我必须手动指定文件:compasswatchpublic/stylesheets/myfile.sass等。如何让它自动运行?文件ie.css、print.css和screen.css已放在stylesheets/compiled。如何在编译后不让它们重新出现的情况下删除它们?我自己编译的.sass文件编译成compiled/t

  9. ruby - 使用 ruby​​ 将 HTML 转换为纯文本并维护结构/格式 - 2

    我想将html转换为纯文本。不过,我不想只删除标签,我想智能地保留尽可能多的格式。为插入换行符标签,检测段落并格式化它们等。输入非常简单,通常是格式良好的html(不是整个文档,只是一堆内容,通常没有anchor或图像)。我可以将几个正则表达式放在一起,让我达到80%,但我认为可能有一些现有的解决方案更智能。 最佳答案 首先,不要尝试为此使用正则表达式。很有可能你会想出一个脆弱/脆弱的解决方案,它会随着HTML的变化而崩溃,或者很难管理和维护。您可以使用Nokogiri快速解析HTML并提取文本:require'nokogiri'h

  10. ruby - 在 64 位 Snow Leopard 上使用 rvm、postgres 9.0、ruby 1.9.2-p136 安装 pg gem 时出现问题 - 2

    我想为Heroku构建一个Rails3应用程序。他们使用Postgres作为他们的数据库,所以我通过MacPorts安装了postgres9.0。现在我需要一个postgresgem并且共识是出于性能原因你想要pggem。但是我对我得到的错误感到非常困惑当我尝试在rvm下通过geminstall安装pg时。我已经非常明确地指定了所有postgres目录的位置可以找到但仍然无法完成安装:$envARCHFLAGS='-archx86_64'geminstallpg--\--with-pg-config=/opt/local/var/db/postgresql90/defaultdb/po

随机推荐