6
votes

I'm using antd picture-wall/card example to upload images to my firebase storage with this reference code and the only place I'm changing is action property on <Upload> component.

On the action property, I'm using a function that uploads the images to firebase storage instead of a link both are accepted as seen in docs.

My action function looks like this;

export async function uploadImage(file) {
    const storage = firebase.storage()
    const metadata = {
        contentType: 'image/jpeg'
    }
    const storageRef = await storage.ref()
    const imageName = generateHashName() //a unique name for the image
    const imgFile = storageRef.child(`Vince Wear/${imageName}.png`)
    return imgFile.put(file, metadata)
}

Issue comes, The image uploads to firebase successfully, but I keep getting antd response handling errors and possibly not sure what action function should return, even though, is written in the docs that it should return a promise.

Error message:

XML Parsing Error: syntax error
Location: http://localhost:3000/[object%20Object]
Line Number 1, Column 1:

Errors also appear as a red border on the uploaded image thumbnail.

Requested help, What should my action function return to get rid of errors. I can parse my firebase response and return the necessary details to antd upload action.

Using

    "antd": "^3.9.2",
    "firebase": "^5.8.5",
    "react": "^16.7.0",
2
try to create a new object of the imgFile type instead of injecting it from the dom oOjonathan Heindl
@jonathanHeindl Why, I don't believe that is the issue since the image successfully uploads to Firebase.ArchNoob
oh sry I missed that :( have you already tried other standard return values ? like true or void ?jonathan Heindl
btw I checked the source code type defeinition (which is not very helpful :/ ) action?: string | ((file: UploadFile) => PromiseLike<any>);jonathan Heindl
ok antd just passes the upload to rc-upload in version 2.6.0 (!! if the dependency resolver asumed it could upgrade to the most recent version action functions are handled the same ) but in version 2.6.0 thers 2 options : either it gets handled as an ajax uploader which asumes an url to be returned (since it directly puts the return value in a request as url ) or the iframeuplaoder which just adds it as a form Attribute oO , I suggest trying an url like www.google.de for testing even though Im not sure which http MEthod it expects at this momentjonathan Heindl

2 Answers

17
votes

You can use customRequest prop to fix this issue. Have a look

class CustomUpload extends Component {
  state = { loading: false, imageUrl: '' };
  
  handleChange = (info) => {
    if (info.file.status === 'uploading') {
      this.setState({ loading: true });
      return;
    }
    if (info.file.status === 'done') {
      getBase64(info.file.originFileObj, imageUrl => this.setState({
        imageUrl,
        loading: false
      }));
    }
  };

  beforeUpload = (file) => {
    const isImage = file.type.indexOf('image/') === 0;
    if (!isImage) {
      AntMessage.error('You can only upload image file!');
    }
    
    // You can remove this validation if you want
    const isLt5M = file.size / 1024 / 1024 < 5;
    if (!isLt5M) {
      AntMessage.error('Image must smaller than 5MB!');
    }
    return isImage && isLt5M;
  };

  customUpload = ({ onError, onSuccess, file }) => {
    const storage = firebase.storage();
    const metadata = {
        contentType: 'image/jpeg'
    }
    const storageRef = await storage.ref();
    const imageName = generateHashName(); //a unique name for the image
    const imgFile = storageRef.child(`Vince Wear/${imageName}.png`);
    try {
      const image = await imgFile.put(file, metadata);
      onSuccess(null, image);
    } catch(e) {
      onError(e);
    }
  };
  
  render () {
    const { loading, imageUrl } = this.state;
    const uploadButton = (
    <div>
      <Icon type={loading ? 'loading' : 'plus'} />
      <div className="ant-upload-text">Upload</div>
    </div>
    );
    return (
      <div>
        <Upload
          name="avatar"
          listType="picture-card"
          className="avatar-uploader"
          beforeUpload={this.beforeUpload}
          onChange={this.handleChange}
          customRequest={this.customUpload}
        >
          {imageUrl ? <img src={imageUrl} alt="avatar" /> : uploadButton}
        </Upload>
      </div>
    );
  }
}
2
votes

Just leaving this here incase anyone wanted to track the progress of the file aswell

 const customUpload = async ({ onError, onSuccess, file, onProgress }) => {
    let fileId = uuidv4()
    const fileRef = stg.ref('demo').child(fileId)
    try {
      const image = fileRef.put(file, { customMetadata: { uploadedBy: myName, fileName: file.name } })

      image.on(
        'state_changed',
        (snap) => onProgress({ percent: (snap.bytesTransferred / snap.totalBytes) * 100 }),
        (err) => onError(err),
        () => onSuccess(null, image.metadata_)
      )
    } catch (e) {
      onError(e)
    }
  }