deno 怎么把fetch得到的图片数据存到本地, 怎么直接通过服务response返回

有没类似得demo啊, 求大佬给一个

import { copy, readerFromStreamReader} from "https://deno.land/std/streams/mod.ts";

// fetch 远程图片
const response = await fetch("https://deno.com/deploy/logo.png");
// 打开本地文件
const file = await Deno.open("./logo.png", { create: true, write: true });
// 从 response 创建可读流
const reader = readerFromStreamReader(response.body!.getReader());
// 将可读流 copy 到 file 中
await copy(reader, file);
// 关闭文件
file.close();

运行:

deno run --allow-net --allow-write fetch.ts

不使用 steam 的方法:

const response = await fetch("https://deno.com/deploy/logo.png");
const buffer = await response.arrayBuffer();
await Deno.writeFile("./logo.png", new Uint8Array(buffer));
  • Stream 方式:一边 fetch 一边写入文件(如果服务器支持)
  • 普通方式:先 fetch,保存到内存,然后一次性 write 到文件
1 个赞

:+1: 好详细, 非常感谢

stream 的方式走response好像不太对, 不知道咋写啦

import { serve } from "https://deno.land/std@0.119.0/http/server.ts";
import {  readerFromStreamReader} from "https://deno.land/std/streams/mod.ts";
const respOpt = {
      status: 200,
      headers: {
        "content-type": "image/png",
      },
    }
async function handleRequest(request) {
   const { pathname } = new URL(request.url);
   const response = await fetch('https://deno.com/deploy/logo.png')

  if (pathname.startsWith("/pic")) {
    const fileBuf = await response.arrayBuffer()
    return new Response(new Uint8Array(fileBuf), respOpt);
  }

  if(pathname.startsWith('/img')) {
      const reader = readerFromStreamReader(response.body?.getReader());
      // Stream方式, 应该怎么写呀,
      return new Response(reader, respOpt);
      // request.respondWith(
        new Response(reader, respOpt)
      )
  }
}
console.log("Listening on http://localhost:8000");
serve(handleRequest);

fetch拿到之后, 怎么pipe/copy到response里去呀

import { readableStreamFromReader } from 'https://deno.land/std@0.119.0/streams/conversion.ts';
import { readerFromStreamReader } from "https://deno.land/std/streams/mod.ts";
import { fromFileUrl } from 'https://deno.land/std@0.119.0/path/mod.ts';
const server = Deno.listen({ port: 8080 });
console.log('chat server starting on :8080....');
const respOpt = {
  status: 200,
  headers: {
    'content-type': 'image/png',
  },
};

async function requestHandler(req) {
  const response = await fetch('https://deno.com/deploy/logo.png');
  
  const pathname = new URL(req.request.url).pathname;
  console.log(pathname,'pathname')
  if (pathname == '/img') {
    // 这部分咋写呀, 有可行方案嘛
    const reader = readerFromStreamReader(response.body?.getReader());
    req.respondWith(new Response(reader, respOpt));
  }
  if( req.request.method === "GET" && pathname === "/favicon.ico") {
      req.respondWith(Response.redirect("https://deno.land/favicon.ico", 302));
  }
  if (pathname == '/pic') {
    const u = new URL("./logo.png", import.meta.url);
    const file = await Deno.open(fromFileUrl(u));
    req.respondWith(
        new Response(readableStreamFromReader(file), respOpt),
    );
  }
}
for await (const conn of server) {
  (async () => {
    const httpConn = Deno.serveHttp(conn);
    for await (const requestEvent of httpConn) {
      requestHandler(requestEvent);
    }
  })();
}

直接 return 就行了啊。fetch 的结果就是 Response 对象。

safe-newt-12 - Deploy Playground (deno.com)

import { serve } from "https://deno.land/std@0.119.0/http/server.ts";

// deno-lint-ignore no-unused-vars
async function handler(request: Request): Promise<Response> {
  return await fetch("https://deno.com/deploy/logo.png");
}

console.log("Listening on http://localhost:8000");
serve(handler);
1 个赞

nice, 没想到能这么简单

1 个赞

我特意标准了类型,handler 函数返回一个 Promise<Response>,而 fetch 的类型就是:

function fetch(input: Request): Promise<Response>

恰恰返回了一个 Promise<Response>,所以,直接把 fetch 的返回值 return 就行了。其实,async/await 可以直接去掉的。

另外,handler 的参数是 Request,fetch 的参数也是,所以如果我们想做一个代理服务的话,也是非常简单的。

试了一下这个return fetch做http代理的操作, 发现可行, 不过好像只能走通http请求
curl.exe -x localhost:8000 http://httpbin.org/get?a=1 -v
跑不通https的
curl.exe -x localhost:8000 https://httpbin.org/get?a=1 -v
用tcp应该能实现 https请求的代理, 不过也失败啦, 好像是在最后互相copy阶段出现的问题, 懵p了又

// curl.exe -x localhost:8000  https://httpbin.org/get?a=1 -k -v
const listener = Deno.listen({ port: 8000 })
console.log('http://localhost:8000/')

function parseHeaders (headers) {
  var arrhs = headers.split('\r\n'),
    arrh1 = arrhs[0].split(' '),
    method = arrh1[0].trim(),
    arrPath,
    host,
    port
  if (method == 'CONNECT') {
    arrPath = arrh1[1].split(':')
    host = arrPath[0]
    port = arrPath[1] || 443
  } else {
    arrPath = arrhs[1]?.split(' ')[1].split(':')
    host = arrPath[0]
    port = arrPath[1] || 80
  }
  return [method, host, port]
}
for await (const conn of listener) {
  ;(async () => {
    let buf = new Uint8Array(4096)
    let n = (await conn.read(buf)) || 0
    if (n == 0) return
    buf = buf.slice(0, n)
    let headers = new TextDecoder().decode(buf)
    console.log(headers)
    if (!headers.includes('\r\n\r\n')) return
    let [method, host, port] = parseHeaders(headers)
    console.log(method, host, port)
    const isTls = method == 'CONNECT'

    let connect = isTls ? 'connectTls' : 'connect'
    var remote = await Deno[connect]({
      hostname: host,
      port,
      transport: 'tcp'
    })
    if (isTls) {
      let head =
        'HTTP/1.1 200 Connection established\r\nConnection: close\r\n\r\n'
      await conn.write(new TextEncoder().encode(head))
    } else {
      await remote.write(buf)
    }
    Deno.copy(conn, remote)
    await Deno.copy(remote, conn)
    // 似乎是这里出了问题, 也是可以走通http的请求, 跑不通https的请求
  })()
}

访问 http://localhost:8000 会全站镜像 github:

import { serve } from "https://deno.land/std@0.120.0/http/server.ts";
import { blue } from "https://deno.land/std@0.120.0/fmt/colors.ts";

const FROM = "http://localhost:8000";
const TO = "https://github.com";

async function handleRequest(request: Request): Promise<Response> {
  const url = request.url.replace(FROM, TO); // 原 url 替换为代理 url
  const response = await fetch(url);

  const headers = new Headers(response.headers);
  headers.set("x-proxy-by", "xxx");
  headers.set("x-custom", "yyy");

  const body = (await response.text()).replaceAll(TO, FROM);
  return new Response(body, { headers });
}

console.log(`Proxy ${blue(FROM)} to ${TO}`);
serve(handleRequest);

:+1: