Node.js AWS S3-无法显示图像,因为它包含错误

Node.js AWS S3-无法显示图像,因为它包含错误,node.js,reactjs,amazon-web-services,amazon-s3,image-uploading,Node.js,Reactjs,Amazon Web Services,Amazon S3,Image Uploading,我目前正在用React、Redux、JavaScript、Node和Adonisjs构建一个注册表单。我正在尝试将配置文件图像上载到我的AWS s3存储桶。目前,我已经得到了它,所以文件出现在桶中。但是,当我尝试单击AWS s3提供的链接时,我得到以下错误 我已将权限设置为public,并提供了正确的凭据密钥。我被困在如何解决这个问题,所以任何帮助都将不胜感激。请参阅下面的代码 下面是我的AWS控制器: "use strict"; require("dotenv").config(); con

我目前正在用React、Redux、JavaScript、Node和Adonisjs构建一个注册表单。我正在尝试将配置文件图像上载到我的AWS s3存储桶。目前,我已经得到了它,所以文件出现在桶中。但是,当我尝试单击AWS s3提供的链接时,我得到以下错误

我已将权限设置为public,并提供了正确的凭据密钥。我被困在如何解决这个问题,所以任何帮助都将不胜感激。请参阅下面的代码

下面是我的AWS控制器:

"use strict";

require("dotenv").config();
const aws = require("aws-sdk");
const fs = require("fs");
const Helpers = use("Helpers");
const Drive = use("Drive");

class awsController {
  async upload({ request, response }) {
    aws.config.update({
      region: "ap-southeast-2", // AWS region
      accessKeyId: process.env.S3_KEY,
      secretAccessKey: process.env.S3_SECERT
    });

    const S3_BUCKET = process.env.S3_BUCKET;

    const s3Bucket = new aws.S3({
      region: "ap-southeast-2",
      accessKeyId: process.env.S3_KEY,
      secretAccessKey: process.env.S3_SECERT,
      Bucket: process.env.S3_BUCKET
    }); // Create a new instance of S3

    const fileName = request.all().body.fileName;
    console.log(fileName);
    const fileType = request.all().body.fileType;

    const params = {
      Bucket: S3_BUCKET,
      Key: fileName,
      ContentType: fileType,
      ACL: "public-read"
    };

    s3Bucket.putObject(params, function(err, data) {
      if (err) {
        response.send(err);
      }
      console.log(data);

      const returnData = {
        signedRequest: data,
        url: `https://${S3_BUCKET}.s3.amazonaws.com/${fileName}`
      };

      console.log(returnData);
      response.send("Success");
    });
  }
}

module.exports = awsController;

**下面处理文件的上载**


import React, { Component } from "react";
import axiosAPI from "./../resorterAPI";
import axios from "axios";

class ImageUpload extends Component {
  constructor(props) {
    super(props);
    this.state = {
      success: false,
      url: ""
    };
  }

  handleChange = event => {};

  handleUpload = event => {
    let uploadedFile = this.uploadInput.files[0];

    // Splits name of file
    let fileParts = this.uploadInput.files[0].name.split(".");
    let fileName = fileParts[0];
    let fileType = fileParts[1];
    let fullFileName = uploadedFile.name;

    console.log("preparing upload");

    axiosAPI
      .post("/s3Upload", {
        body: {
          fileName:
            Math.round(Math.random() * 1000 * 300000).toString() +
            "_" +
            uploadedFile.name,
          fileType: fileType
        }
      })
      .then(response => {
        console.log("Success");
      });
  };

  render() {
    return (
      <div>
        <center>
          <input
            onChange={this.handleChange}
            ref={ref => {
              this.uploadInput = ref;
            }}
            type="file"
          />
          <br />
          <button onClick={this.handleUpload}> Upload </button>
        </center>
      </div>
    );
  }
}

export default ImageUpload;


从“React”导入React,{Component};
从“/。/resorterAPI”导入axiosAPI;
从“axios”导入axios;
类ImageUpload扩展组件{
建造师(道具){
超级(道具);
此.state={
成功:错,
网址:“
};
}
handleChange=event=>{};
handleUpload=事件=>{
让uploadedFile=this.uploadInput.files[0];
//拆分文件名
让fileParts=this.uploadInput.files[0].name.split(“.”);
让fileName=fileParts[0];
让fileType=fileParts[1];
让fullFileName=uploadedFile.name;
控制台日志(“准备上传”);
axiosAPI
.post(“/s3Upload”{
正文:{
文件名:
Math.round(Math.random()*1000*300000).toString()+
"_" +
uploadedFile.name,
fileType:fileType
}
})
。然后(响应=>{
控制台日志(“成功”);
});
};
render(){
返回(
{
this.uploadInput=ref;
}}
type=“文件”
/>

上传 ); } } 导出默认图像上传;
**下面是调用图像上传服务的地方**

import React from "react";

// Redux
import { Field, reduxForm } from "redux-form";
import { connect } from "react-redux";
import { getCountry } from "./../../redux/actions/getCountryAction.js";
import Store from "./../../redux/store.js";

// Services
import axiosAPI from "./../../api/resorterAPI";
import ImageUpload from "./../../api/services/ImageUpload";

// Calls a js file with all area codes matched to countries.
import phoneCodes from "./../../materials/PhoneCodes.json";

// Component Input
import {
  renderField,
  renderSelectField,
  renderFileUploadField
} from "./../FormField/FormField.js";

// CSS
import styles from "./signupForm.module.css";
import classNames from "classnames";

function validate(values) {
  let errors = {};

  if (!values.specialisations) {
    errors.firstName = "Required";
  }

  if (!values.resort) {
    errors.lastName = "Required";
  }

  if (!values.yearsOfExperience) {
    errors.email = "Required";
  } else if (!/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$/i.test(values.email)) {
    errors.email = "Invalid Email";
  }

  if (!values.spokenLanguages) {
    errors.password = "Required";
  }

  if (values.sport !== values.confirmPassword) {
    errors.confirmPassword = "Passwords don't match";
  }

  return errors;
}

class SignupFormStepTwo extends React.Component {
  constructor(props) {
    super(props);

    this.state = {
      countries: [],
      selectedCountry: [],
      dialCodes: []
    };
  }

  // Below gets the countries from axios call made in redux actions
  async componentDidMount() {
    const countries = await getCountry();
    // The below maps through the json file and gets all the dial codes
    const codes = phoneCodes.phoneCode.map(code => {
      return code.dial_code;
    });
    return this.setState({ countries: countries, dialCodes: codes });
  }

  // Handles when someone changes the select fields
  handleChange = event => {
    const selectedCountry = event.target.value;
    return this.setState({ selectedCountry: selectedCountry });
  };

  // uploadFile = (values, url = "") => {
  //   axiosAPI.post("/displayImageUpload", { ...values, imageURL: url}).then(response => {
  //     console.log("Success");
  //   })
  // }

  render() {
    return (
      <div>
        <form onSubmit={this.props.handleSubmit}>
          <div className={styles.signupContainer}>
            <div className={styles.signupInputContainer}>
              {/* <Field
                name="displayImage"
                component={renderFileUploadField}
                type="text"
                label="Display Image"
              /> */}

              <ImageUpload />

              <div
                className={classNames({
                  [styles.bioField]: true,
                  [styles.signupInputContainer]: true
                })}
              >
                <Field
                  name="bio"
                  component={renderField}
                  type="text"
                  label="Bio"
                  placeholder="Introduce yourself - Where you're from, what your hobbies are, etc..."
                />
              </div>
            </div>

            {/* Renders the select field  */}
            <div className={styles.signupInputContainer}>
              <Field
                name="specialisations"
                component={renderSelectField}
                type="select"
                label="Specialisations"
                placeholder="Specialisations"
                options={this.state.countries}
                onChange={this.handleChange}
                multiple={false}
              />

              <Field
                name="resort"
                component={renderSelectField}
                options={this.state.dialCodes}
                type="select"
                label="Resort"
                placeholder="Select the resorts you can work at"
              />

              <Field
                name="yearsOfExperience"
                component={renderSelectField}
                options={this.state.dialCodes}
                type="select"
                label="Years of Experience"
              />

              <Field
                name="spokenLanguages"
                component={renderSelectField}
                options={this.state.dialCodes}
                type="select"
                label="Spoken Languages"
              />
            </div>
          </div>
          <div className={styles.signupButtonContainer}>
            <button className={styles.signupButton} type="submit">
              {" "}
              Submit{" "}
            </button>
          </div>
        </form>
      </div>
    );
  }
}

// destroyOnUnmount - saves it in state
SignupFormStepTwo = reduxForm({
  form: "signupStageTwo",
  destroyOnUnmount: false,
  validate
})(SignupFormStepTwo);

const mapStateToProps = state => {
  return {
    form: state.form
  };
};

export default SignupFormStepTwo;

从“React”导入React;
//重演
从“redux表单”导入{Field,reduxForm};
从“react redux”导入{connect};
从“/./../redux/actions/getCountryAction.js”导入{getCountry}”;
从“/./../redux/Store.js”导入存储;
//服务
从“/./../api/resorterAPI”导入axiosAPI;
从“/./../api/services/ImageUpload”导入ImageUpload;
//调用包含与国家/地区匹配的所有区号的js文件。
从“/./../materials/phoneCodes.json”导入电话码;
//组件输入
进口{
伦德菲尔德,
renderSelectField,
renderFileUploadField
}来自“/。/FormField/FormField.js”;
//CSS
从“/signupForm.module.css”导入样式;
从“类名”中导入类名;
函数验证(值){
让错误={};
if(!values.specializations){
errors.firstName=“必需”;
}
如果(!values.resort){
errors.lastName=“必需”;
}
如果(!values.yearsof experience){
errors.email=“必需”;
}如果(!/^[A-Z0-9.%+-]+@[A-Z0-9.-]+\[A-Z]{2,4}$/i.test(values.email)){
errors.email=“无效电子邮件”;
}
如果(!values.spokenLanguages){
errors.password=“必需”;
}
if(values.sport!==values.confirmPassword){
errors.confirmPassword=“密码不匹配”;
}
返回错误;
}
类SignupFormStep2扩展了React.Component{
建造师(道具){
超级(道具);
此.state={
国家:[],
所选国家:[],
拨号代码:[]
};
}
//下面是redux操作中axios调用的国家/地区
异步组件didmount(){
const countries=等待getCountry();
//下面的代码映射到json文件并获取所有拨号代码
const code=phoneCode.phoneCode.map(代码=>{
返回代码。拨号代码;
});
返回此.setState({countries:countries,dialCodes:codes});
}
//当有人更改“选择”字段时处理
handleChange=事件=>{
const selectedCountry=event.target.value;
返回此.setState({selectedCountry:selectedCountry});
};
//uploadFile=(值,url=”“)=>{
//post(“/displayImageUpload”,{…值,imageURL:url})。然后(响应=>{
//控制台日志(“成功”);
//   })
// }
render(){
返回(
{/*  */}
{/*呈现选择字段*/}
{" "}
提交{“}
);
}
}
//destroyOnUnmount-将其保存为状态
SignupFormStep2=reduxForm({
表格:“signupstagewo”,
DestroyOnMont:false,
验证
})(第二步注册);
常量mapStateToProps=状态=>{
返回{
表单:state.form
};
};
导出默认注册表单第二步;

更新:由于Jarmod解决了错误,我实际上没有将文件扩展名与文件名分离,因此它上载image.png.png。

图像类型是否与文件扩展名和/或内容类型不匹配?你能下载文件并验证它的内容实际上是JPEG吗?嘿@Jarmod,我正在将内容类型设置为当前上传的图像的任何类型。那么,这不应该总是确保它匹配吗?如您所见,我正在通过ContentType传递fileType。这回答了你的问题吗?@jarmod-你是对的,它上载的文件名带有扩展名,所以它上载的是image.png.png。我已经解决了这个错误,但是现在文件没有内容,所以它出现在bucket中,但它是一个空文本文档。我将为此问题创建一个新帖子。谢谢您的帮助。您的
putObject
呼叫没有正文。