JavaScript 下载链接图片后上传

2,030 阅读2分钟

JavaScript 下载链接图片后上传

   既然要进行图片上传,那么第一时间当然是判断是否为可下载的图片资源,有的时候可以使用正则表达式,但是很难判断是否可下载,当判断图片链接后是否有后缀的时候也比较苦恼,有的图片是没有后缀的,可是如果放开这个限制又比较容易被攻击,所以这里我们使用 Image 作为判断手法,若成功加载图片,那么说明确实为图片且可下载。

// 判断链接是否指向图片且可下载
export const checkImgExists = (imgurl: string) => {
  return new Promise(function (resolve, reject) {
    var ImgObj = new Image();
    ImgObj.src = imgurl;
    ImgObj.onload = function (res) {
      resolve(res);
    };
    ImgObj.onerror = function (err) {
      reject(err);
    };
  });
};

// how to use it
checkImgExists(imgLink)
  .then(() => {
    // do something with imgLink
    console.log(imgLink);
  })
  .catch((err) => {
    // some log or alarm
    console.log(err);
    console.log("很抱歉, 该链接无法获取图片");
  });

   判断好后,我们需要对这个图片进行下载,这里我们使用 XMLHttpRequest 进行请求下载,下载后会是一个 Blob 对象。

   Blob 本身可以转化成 FormData 对象或者是 File 对象,我们可以根据自己项目的具体情况去选择上传策略,如果想传到 OSS 上,可以选择转化为 File 对象,若是传输到自己的服务器上,可以使用 Ajax,并转 Blob 为 FormData 进行上传。

// 将图片链接中的图片进行 XMLHttpRequest 请求,返回Blob对象
function getImageBlob(url: string) {
  return new Promise(function (resolve, reject) {
    var xhr = new XMLHttpRequest();
    xhr.open("get", url, true);
    xhr.responseType = "blob";
    xhr.onload = function () {
      if (this.status == 200) {
        resolve(this.response);
      }
    };
    xhr.onerror = reject;
    xhr.send();
  });
}

// 将Blob对象转为File对象
const blobToFile = (blob: Blob, fileName: string) => {
  return new window.File([blob], fileName, { type: blob.type });
};

// how to use it
// 返回一个File对象,可使用该 File 对象进行上传操作
getImageBlob(src).then(async (res: any) => {
  const srcSplit = src.split("/");
  const filename = srcSplit[srcSplit.length - 1];
  return blobToFile(res, filename);
});

   接下来是一个上传OSS的小演示,由于 OSS 涉及的隐私信息较多,所以建议大家把accessKeyId、accessKeySecret等信息用接口去获取,甚至使用临时的钥匙等。

import OSS from "ali-oss";

const ERROR_TIP = "上传失败!";

/**
 * File上传OSS的示例
 * 相关accessKeyId、bucket等参数需要根据你的OSS库进行填写
 * 建议将【accessKeyId,accessKeySecret】这两个敏感信息做成接口获取或者加密
 */
export const uploadToOSS = async (
  fileName: string,
  file: File,
  accessKeyId: string,
  accessKeySecret: string,
  ...props
) => {
  let client = new OSS({
    endpoint, // 你申请好的oss项目地址
    bucket, // OSS 对象载体
    accessKeyId, // your accessKeyId with OSS
    accessKeySecret, // your accessKeySecret with OSS
    internal: true,
    ...props,
  });
  const putResult = await client.put(fileName, file, {
    timeout: 60 * 1000 * 2,
  });
  if (putResult.res.status === 200) {
    return { url: putResult.url, fileName };
  }
  throw new Error(ERROR_TIP);
};

   当然如果想上传图片到你自己的服务器,可以选择将 Blob 格式的文件转为 FormData 格式,使用 XMLHttpRequest 或者 Ajax 进行图片的上传

// 将Blob对象转为FormData对象
const blobToFormData = (blob: Blob, fileName: string) => {
  const formdata = new FormData();
  formdata.append("file", blob, fileName);
  return formdata;
};

// XMLHttpRequest
const uploadFile = (formData: FormData) => {
  const url = "your_interface";
  let xhr = new XMLHttpRequest();
  xhr.onload = function () {
    console.log("ok");
    console.log(JSON.parse(xhr.responseText));
  };
  xhr.onerror = function () {
    console.log("fail");
  };
  xhr.open("post", url, true);
  xhr.send(formData);
};

// Ajax
const uploadFile2 = (formData: FormData) => {
  const url = "your_interface";
  $.ajax({
    url,
    type: "POST",
    data: formData,
    async: false,
    cache: false,
    contentType: false,
    processData: false,
    success: function (returndata) {
      console.log(returndata);
    },
    error: function (returndata) {
      console.log(returndata);
    },
  });
};

   在之前我的后端项目中,使用了 Express 作为静态图片库,以下是我的 node 上传图片代码。值得注意的是,使用 formidable 解析后,jpg 文件会直接在你的预设照片目录有一个很长的随机名称,这边其实我也是使用了较短的名称进行重命名,大家可以根据自己的需要选择重命名策略。

const express = require("express");
const listenNumber = 5000;
const app = express();
const bodyParser = require("body-parser");
const http = require("http"); //创建服务器的
const formidable = require("formidable");
const path = require("path");
const fs = require("fs");
app.use(express.static("../../upload"));
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json()); //数据JSON类型

// 上传图片
app.post("/upLoadArticlePicture", (req, res, next) => {
  let defaultPath = "../../upload/";
  let uploadDir = path.join(__dirname, defaultPath);
  let form = new formidable.IncomingForm();
  let getRandomID = () =>
    Number(Math.random().toString().substr(4, 10) + Date.now()).toString(36);
  form.uploadDir = uploadDir; //设置上传文件的缓存目录
  form.encoding = "utf-8"; //设置编辑
  form.keepExtensions = true; //保留后缀
  form.maxFieldsSize = 2 * 1024 * 1024; //文件大小
  form.parse(req, function (err, fields, files) {
    if (err) {
      res.locals.error = err;
      res.render("index", { title: TITLE });
      return;
    }
    let filePath = files.file["path"];
    let backName = filePath.split(".")[1];
    let oldPath = filePath.split("\\")[filePath.split("\\").length - 1];
    let newPath = `${getRandomID()}.${backName}`;
    fs.rename(defaultPath + oldPath, defaultPath + newPath, (err) => {
      if (!err) {
        newPath = `http://localhost:${listenNumber}/${newPath}`;
        res.json({ flag: true, path: newPath });
      } else {
        res.json({ flag: false, path: "" });
      }
    });
  });
});