J'ai un désordre complet d'un composant. En ce moment, je passe une fonction que j'ai essayé un million de choses que je ne peux pas faire fonctionner.
export default class DatafileUpload extends Component {
initialState = {
fileUploading: false,
fileList: [],
status: 'empty', // 'empty' | 'active' | 'success' | 'exception'
file: {}
}
state = this.initialState
static propTypes = {
userId: PropTypes.string.isRequired,
datasetId: PropTypes.string.isRequired
}
scrubFilename = (filename) => filename.replace(/[^\w\d_\-.]+/ig, '')
requestSignedS3Url = (file) => {
const filename = this.scrubFilename(file.name)
const params = {
userId: this.props.userId,
contentType: file.type,
Key: `${filename}`
};
return api.get('/s3/signUpload', { params })
.then(response => {
return response.data;
})
.catch(error => {
console.error(error);
});
}
uploadFile = (file) => {
this.requestSignedS3Url(file)
.then(signResult => this.uploadToS3(file, signResult))
.catch(error => console.log(error))
}
createCORSRequest = (method, url, opts) => {
opts = opts || {};
let xhr = new XMLHttpRequest();
if (xhr.withCredentials != null) {
xhr.open(method, url, true);
if (opts.withCredentials != null) {
xhr.withCredentials = opts.withCredentials;
}
} else if (typeof XDomainRequest !== "undefined") {
xhr = new XDomainRequest();
xhr.open(method, url);
} else {
xhr = null;
}
return xhr;
};
stepFunctions = () => {
return {
preprocess: (file) => {
console.log('Pre-process: ' + file.name);
},
onProgress: (percent, message, file) => {
this.setState({ fileUploading: true })
console.log('Upload progress: ' + percent + '% ' + message);
},
onFinish: (signResult) => {
this.setState({ fileUploading: false })
console.log("Upload finished: " + signResult.publicUrl)
},
onError: (message) => {
this.setState({ fileUploading: false })
console.log("Upload error: " + message);
},
scrubFilename: (filename) => {
return filename.replace(/[^\w\d_\-\.]+/ig, '');
},
onFinishS3Put: (signResult, file) => {
console.log(signResult)
return console.log('base.onFinishS3Put()', signResult.publicUrl);
}
}
}
uploadToS3 = async (file, signResult) => {
const xhr = await this.createCORSRequest('PUT', signResult.signedUrl);
const functions = this.stepFunctions()
functions.preprocess(file)
if (!xhr) {
functions.onError('CORS not supported', file);
} else {
xhr.onload = () => {
if (xhr.status === 200) {
functions.onProgress(100, 'Upload completed', file);
return functions.onFinishS3Put('potatopotato', file);
} else {
return functions.onError('Upload error: ' + xhr.status, file);
}
};
xhr.onerror = () => {
return functions.onError('XHR error', file);
};
xhr.upload.onprogress = (e) => {
let percentLoaded;
if (e.lengthComputable) {
percentLoaded = Math.round((e.loaded / e.total) * 100);
return functions.onProgress(percentLoaded, percentLoaded === 100 ? 'Finalizing' : 'Uploading', file);
}
};
}
xhr.setRequestHeader('Content-Type', file.type);
if (signResult.headers) {
const signResultHeaders = signResult.headers
Object.keys(signResultHeaders).forEach(key => {
const val = signResultHeaders[key];
xhr.setRequestHeader(key, val);
})
}
xhr.setRequestHeader('x-amz-acl', 'public-read');
this.httprequest = xhr;
return xhr.send(file);
};
handleChange = ({ file, fileList }) => {
const functions = this.stepFunctions()
functions.preprocess(file)
if (!file) {
functions.onError('CORS not supported', file);
} else {
file.onload = () => {
if (file.status === 200) {
functions.onProgress(100, 'Upload completed', file);
return functions.onFinishS3Put('potatopotato', file);
} else {
return functions.onError('Upload error: ' + file.status, file);
}
};
file.onerror = () => {
return functions.onError('XHR error', file);
};
file.upload.onprogress = (e) => {
let percentLoaded;
if (e.lengthComputable) {
percentLoaded = Math.round((e.loaded / e.total) * 100);
return functions.onProgress(percentLoaded, percentLoaded === 100 ? 'Finalizing' : 'Uploading', file);
}
};
}
console.log('File: ', file)
// always setState
this.setState({ fileList });
}
render() {
const props = {
onChange: this.handleChange,
multiple: true,
name: "uploadFile",
defaultFileList: this.initialState.fileList,
data: this.uploadFile,
listType: "text",
customRequest: ????,
showUploadList: {
showPreviewIcon: true,
showRemoveIcon: true
},
onProgress: ( {percent} ) => {
this.setState({ fileUploading: true })
console.log('Upload progress: ' + percent + '% ' );
},
onError: (error, body) => {
this.setState({ fileUploading: false })
console.log("Upload error: " + error);
},
onSuccess: (body)=> {
console.log(body)
return console.log('base.onFinishS3Put()');
}
};
return (
<Upload {...props} fileList={this.state.fileList}>
<Button>
<Icon type="upload" /> Upload
</Button>
</Upload>
)
}
}
Je sais que ce code est un gâchis qui n'a pas de sens et qui contient des données en double tout autour. Je veux que cela fonctionne, puis nettoyer/optimiser. Fondamentalement, je ne suis pas en mesure de mettre à jour la barre de progression du composant ni avec le onChange
ni lorsque j'essaie d'utiliser le customRequest
. Quand est appelé customRequest
? This n'est pas très abondant dans les explications ... Je ne comprends pas comment cela fait le remplacement du téléchargement Ajax.
Je me débattais aussi avec cela, puis j'ai trouvé votre question.
Donc, la façon dont j'ai trouvé d'utiliser customRequest et onChange est:
<Upload name="file" customRequest={this.customRequest} onChange={this.onChange}>
<Button>
<Icon type="upload" /> Click to Upload
</Button>
</Upload>
...
onChange = (info) => {
const reader = new FileReader();
reader.onloadend = (obj) => {
this.imageDataAsURL = obj.srcElement.result;
};
reader.readAsDataURL(info.file.originFileObj);
...
};
...
customRequest = ({ onSuccess, onError, file }) => {
const checkInfo = () => {
setTimeout(() => {
if (!this.imageDataAsURL) {
checkInfo();
} else {
this.uploadFile(file)
.then(() => {
onSuccess(null, file);
})
.catch(() => {
// call onError();
});
}
}, 100);
};
checkInfo();
};
Il existe probablement de meilleures façons de le faire, mais j'espère que cela vous aidera.
Je me suis beaucoup battu et j'ai trouvé un moyen efficace de gérer cette affaire.
tout d'abord, vous ne devez jouer avec customRequest que lorsque vous devez modifier le corps et le type de demande (comme utiliser post au lieu de 'put' ou utiliser xml ou ajouter un autre en-tête supplémentaire).
pour l'URL de signature, vous pouvez envoyer le rappel d'action prop qui renvoie une promesse avec l'URL appropriée à télécharger comme:
handleUplaod = (file: any) => {
return new Promise(async (resolve, reject) => {
const fileName = `nameThatIwant.type`;
const url = await S3Fetcher.getPresignedUrl(fileName);
resolve(url);
});
et rendre comme:
render(){
return(
....
<Upload
action={this.handleUplaod}
....
Upload>
l'uploader prend l'url de l'action prop.
la méthode onChange qui est également fournie sera appelée chaque fois que le statut du téléchargement est modifié-
onChange # La fonction sera appelée lorsque le téléchargement est en cours, terminé ou a échoué.
Lors du téléchargement d'un changement d'état, il renvoie:
{fichier: {/ * ... /}, fileList: [/ ... /], événement: {/ ... * /},}
lorsque le téléchargement a commencé, vous devrez activer le lecteur de fichiers à partir de là. comme:
....
fileReader = new FileReader();
.....
onChange = (info) => {
if (!this.fileReader.onloadend) {
this.fileReader.onloadend = (obj) => {
this.setState({
image: obj.srcElement.result, //will be used for knowing load is finished.
});
};
// can be any other read function ( any reading function from
// previously created instance can be used )
this.fileReader.readAsArrayBuffer(info.file.originFileObj);
}
};
remarquez à la fin de l'étape que l'événement = undefind
Pour mettre à jour l'interface utilisateur à partir des événements de téléchargement, vous devez utiliser les variables d'options de customRequest et les appeler chaque fois que vous en avez besoin.
onSuccess- doit être appelé lorsque vous avez terminé le téléchargement et il changera l'icône de chargement en nom de fichier.
onError- peindra le nom de fichier classé en rouge.
onProgress- mettra à jour la barre de progression et devrait être appelé avec {percent: [NUMBER]} pour la mise à jour.
par exemple dans mon code-
customRequest = async option => {
const { onSuccess, onError, file, action, onProgress } = option;
const url = action;
await new Promise(resolve => this.waitUntilImageLoaded(resolve)); //in the next section
const { image } = this.state; // from onChange function above
const type = 'image/png';
axios
.put(url, Image, {
onUploadProgress: e => {
onProgress({ percent: (e.loaded / e.total) * 100 });
},
headers: {
'Content-Type': type,
},
})
.then(respones => {
/*......*/
onSuccess(respones.body);
})
.catch(err => {
/*......*/
onError(err);
});
};
waitUntilImageLoaded = resolve => {
setTimeout(() => {
this.state.image
? resolve() // from onChange method
: this.waitUntilImageLoaded(resolve);
}, 10);
};
J'ai utilisé axios mais vous pouvez aussi utiliser d'autres bibliothèques et le plus important -
render(){
return(
....
<Upload
onChange={this.onChange}
customRequest={this.customRequest}
...>
onCustomRequest = file => {
return new Promise(((resolve, reject) => {
const ajaxResponseWasFine = true;
setTimeout(() => {
if (ajaxResponseWasFine) {
const reader = new FileReader();
reader.addEventListener('load', () => {
resolve(reader.result);
}, false);
if (file) {
reader.readAsDataURL(file);
}
} else {
reject('error');
}
}, 1000);
}));
};
<Dragger
action={this.onCustomRequest}
fileList={fileList}
onChange={this.handleChangeUpload}
className={styles.dragWrapper}
showUploadList={true}
beforeUpload={this.beforeUpload}
>